New drop of the jmonkeyengine library A new drop of the jmonkeyengine library sources, based on a 2012-03-05 snapshot. Fixes a few unnecessary memory allocations in the main rendering loop. Change-Id: I51ac0942fe87204df102cfdce746b59a5cb5ff85 
diff --git a/engine/src/android/com/jme3/app/AndroidHarness.java b/engine/src/android/com/jme3/app/AndroidHarness.java index 6631003..0ad2ba1 100644 --- a/engine/src/android/com/jme3/app/AndroidHarness.java +++ b/engine/src/android/com/jme3/app/AndroidHarness.java 
@@ -70,6 +70,10 @@  */   protected boolean mouseEventsInvertY = true;   /**  + * if true finish this activity when the jme app is stopped  + */  + protected boolean finishOnAppStop = true;  + /**   * Title of the exit dialog, default is "Do you want to exit?"   */   protected String exitDialogTitle = "Do you want to exit?";  @@ -131,7 +135,7 @@  boolean bIsLogFormatSet = false;   do {   if (log.getHandlers().length == 0) {  - log = logger.getParent();  + log = log.getParent();   if (log != null) {   for (Handler h : log.getHandlers()) {   //h.setFormatter(new SimpleFormatter());  @@ -395,4 +399,10 @@  }   }   }  +  + public boolean isFinishOnAppStop() {  + return finishOnAppStop;  + }  +  +   }  
diff --git a/engine/src/android/com/jme3/asset/AndroidAssetManager.java b/engine/src/android/com/jme3/asset/AndroidAssetManager.java index c6f0b17..b2140dd 100644 --- a/engine/src/android/com/jme3/asset/AndroidAssetManager.java +++ b/engine/src/android/com/jme3/asset/AndroidAssetManager.java 
@@ -37,6 +37,7 @@  import com.jme3.texture.Texture;   import com.jme3.texture.plugins.AndroidImageLoader;   import java.net.URL;  +import java.util.logging.Level;   import java.util.logging.Logger;     /**  @@ -57,6 +58,14 @@  //this(Thread.currentThread().getContextClassLoader().getResource("com/jme3/asset/Android.cfg"));   this(null);   }  +  + private void registerLoaderSafe(Class<? extends AssetLoader> loaderClass, String ... extensions) {  + try {  + registerLoader(loaderClass, extensions);  + } catch (Exception e){  + logger.log(Level.WARNING, "Failed to load AssetLoader", e);  + }  + }     /**   * AndroidAssetManager constructor  @@ -67,25 +76,29 @@  System.setProperty("org.xml.sax.driver", "org.xmlpull.v1.sax2.Driver");     // Set Default Android config  - this.registerLocator("", AndroidLocator.class);  - this.registerLocator("", ClasspathLocator.class);  - this.registerLoader(AndroidImageLoader.class, "jpg", "bmp", "gif", "png", "jpeg");  - this.registerLoader(AndroidAudioLoader.class, "ogg", "mp3", "wav");  - this.registerLoader(com.jme3.material.plugins.J3MLoader.class, "j3m");  - this.registerLoader(com.jme3.material.plugins.J3MLoader.class, "j3md");  - this.registerLoader(com.jme3.font.plugins.BitmapFontLoader.class, "fnt");  - this.registerLoader(com.jme3.texture.plugins.DDSLoader.class, "dds");  - this.registerLoader(com.jme3.texture.plugins.PFMLoader.class, "pfm");  - this.registerLoader(com.jme3.texture.plugins.HDRLoader.class, "hdr");  - this.registerLoader(com.jme3.texture.plugins.TGALoader.class, "tga");  - this.registerLoader(com.jme3.export.binary.BinaryImporter.class, "j3o");  - this.registerLoader(com.jme3.scene.plugins.OBJLoader.class, "obj");  - this.registerLoader(com.jme3.scene.plugins.MTLLoader.class, "mtl");  - this.registerLoader(com.jme3.scene.plugins.ogre.MeshLoader.class, "meshxml", "mesh.xml");  - this.registerLoader(com.jme3.scene.plugins.ogre.SkeletonLoader.class, "skeletonxml", "skeleton.xml");  - this.registerLoader(com.jme3.scene.plugins.ogre.MaterialLoader.class, "material");  - this.registerLoader(com.jme3.scene.plugins.ogre.SceneLoader.class, "scene");  - this.registerLoader(com.jme3.shader.plugins.GLSLLoader.class, "vert", "frag", "glsl", "glsllib");  + registerLocator("", AndroidLocator.class);  + registerLocator("", ClasspathLocator.class);  +  + registerLoader(AndroidImageLoader.class, "jpg", "bmp", "gif", "png", "jpeg");  + registerLoader(AndroidAudioLoader.class, "ogg", "mp3", "wav");  + registerLoader(com.jme3.material.plugins.J3MLoader.class, "j3m");  + registerLoader(com.jme3.material.plugins.J3MLoader.class, "j3md");  + registerLoader(com.jme3.shader.plugins.GLSLLoader.class, "vert", "frag", "glsl", "glsllib");  + registerLoader(com.jme3.export.binary.BinaryImporter.class, "j3o");  + registerLoader(com.jme3.font.plugins.BitmapFontLoader.class, "fnt");  +  + // Less common loaders (especially on Android)  + registerLoaderSafe(com.jme3.texture.plugins.DDSLoader.class, "dds");  + registerLoaderSafe(com.jme3.texture.plugins.PFMLoader.class, "pfm");  + registerLoaderSafe(com.jme3.texture.plugins.HDRLoader.class, "hdr");  + registerLoaderSafe(com.jme3.texture.plugins.TGALoader.class, "tga");  + registerLoaderSafe(com.jme3.scene.plugins.OBJLoader.class, "obj");  + registerLoaderSafe(com.jme3.scene.plugins.MTLLoader.class, "mtl");  + registerLoaderSafe(com.jme3.scene.plugins.ogre.MeshLoader.class, "mesh.xml");  + registerLoaderSafe(com.jme3.scene.plugins.ogre.SkeletonLoader.class, "skeleton.xml");  + registerLoaderSafe(com.jme3.scene.plugins.ogre.MaterialLoader.class, "material");  + registerLoaderSafe(com.jme3.scene.plugins.ogre.SceneLoader.class, "scene");  +     logger.info("AndroidAssetManager created.");   }  
diff --git a/engine/src/android/com/jme3/asset/plugins/AndroidLocator.java b/engine/src/android/com/jme3/asset/plugins/AndroidLocator.java index 01b1cab..225faf3 100644 --- a/engine/src/android/com/jme3/asset/plugins/AndroidLocator.java +++ b/engine/src/android/com/jme3/asset/plugins/AndroidLocator.java 
@@ -4,9 +4,6 @@  import com.jme3.system.android.JmeAndroidSystem;  import java.io.IOException;  import java.io.InputStream; -import java.net.URL; -import java.net.URLConnection; -import java.util.logging.Level;  import java.util.logging.Logger;    public class AndroidLocator implements AssetLocator { @@ -80,7 +77,7 @@  assetPath = assetPath.replace("//", "/");  try {  return create(manager, key, assetPath); - }catch (IOException ex){ + } catch (IOException ex) {  // This is different handling than URL locator  // since classpath locating would return null at the getResource()   // call, otherwise there's a more critical error... 
diff --git a/engine/src/android/com/jme3/audio/plugins/AndroidAudioLoader.java b/engine/src/android/com/jme3/audio/plugins/AndroidAudioLoader.java index fc9c03e..a11425b 100644 --- a/engine/src/android/com/jme3/audio/plugins/AndroidAudioLoader.java +++ b/engine/src/android/com/jme3/audio/plugins/AndroidAudioLoader.java 
@@ -5,15 +5,16 @@  import com.jme3.audio.android.AndroidAudioData;   import java.io.IOException;    -public class AndroidAudioLoader implements AssetLoader  -{  +/**  + * <code>AndroidAudioLoader</code> will create an  + * {@link AndroidAudioData} object with the specified asset key.  + */  +public class AndroidAudioLoader implements AssetLoader {     @Override  - public Object load(AssetInfo assetInfo) throws IOException  - {  + public Object load(AssetInfo assetInfo) throws IOException {   AndroidAudioData result = new AndroidAudioData();  - result.setAssetKey( assetInfo.getKey() );  + result.setAssetKey(assetInfo.getKey());   return result;   }  -   }  
diff --git a/engine/src/android/com/jme3/input/android/AndroidInput.java b/engine/src/android/com/jme3/input/android/AndroidInput.java index ca1141d..9bbbe4e 100644 --- a/engine/src/android/com/jme3/input/android/AndroidInput.java +++ b/engine/src/android/com/jme3/input/android/AndroidInput.java 
@@ -1,619 +1,620 @@ -package com.jme3.input.android; - -import android.content.Context; -import android.opengl.GLSurfaceView; -import android.util.AttributeSet; -import android.view.GestureDetector; -import android.view.KeyEvent; -import android.view.MotionEvent; -import android.view.ScaleGestureDetector; -import com.jme3.input.KeyInput; -import com.jme3.input.RawInputListener; -import com.jme3.input.TouchInput; -import com.jme3.input.event.MouseButtonEvent; -import com.jme3.input.event.MouseMotionEvent; -import com.jme3.input.event.TouchEvent; -import com.jme3.input.event.TouchEvent.Type; -import com.jme3.math.Vector2f; -import com.jme3.util.RingBuffer; -import java.util.HashMap; -import java.util.logging.Logger; - -/** - * <code>AndroidInput</code> is one of the main components that connect jme with android. Is derived from GLSurfaceView and handles all Inputs - * @author larynx - * - */ -public class AndroidInput extends GLSurfaceView implements TouchInput, - GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener, ScaleGestureDetector.OnScaleGestureListener { - - final private static int MAX_EVENTS = 1024; - // Custom settings - public boolean mouseEventsEnabled = true; - public boolean mouseEventsInvertX = false; - public boolean mouseEventsInvertY = false; - public boolean keyboardEventsEnabled = false; - public boolean dontSendHistory = false; - // Used to transfer events from android thread to GLThread - final private RingBuffer<TouchEvent> eventQueue = new RingBuffer<TouchEvent>(MAX_EVENTS); - final private RingBuffer<TouchEvent> eventPoolUnConsumed = new RingBuffer<TouchEvent>(MAX_EVENTS); - final private RingBuffer<TouchEvent> eventPool = new RingBuffer<TouchEvent>(MAX_EVENTS); - final private HashMap<Integer, Vector2f> lastPositions = new HashMap<Integer, Vector2f>(); - // Internal - private ScaleGestureDetector scaledetector; - private GestureDetector detector; - private int lastX; - private int lastY; - private final static Logger logger = Logger.getLogger(AndroidInput.class.getName()); - private boolean isInitialized = false; - private RawInputListener listener = null; - private static final int[] ANDROID_TO_JME = { - 0x0, // unknown - 0x0, // key code soft left - 0x0, // key code soft right - KeyInput.KEY_HOME, - KeyInput.KEY_ESCAPE, // key back - 0x0, // key call - 0x0, // key endcall - KeyInput.KEY_0, - KeyInput.KEY_1, - KeyInput.KEY_2, - KeyInput.KEY_3, - KeyInput.KEY_4, - KeyInput.KEY_5, - KeyInput.KEY_6, - KeyInput.KEY_7, - KeyInput.KEY_8, - KeyInput.KEY_9, - KeyInput.KEY_MULTIPLY, - 0x0, // key pound - KeyInput.KEY_UP, - KeyInput.KEY_DOWN, - KeyInput.KEY_LEFT, - KeyInput.KEY_RIGHT, - KeyInput.KEY_RETURN, // dpad center - 0x0, // volume up - 0x0, // volume down - KeyInput.KEY_POWER, // power (?) - 0x0, // camera - 0x0, // clear - KeyInput.KEY_A, - KeyInput.KEY_B, - KeyInput.KEY_C, - KeyInput.KEY_D, - KeyInput.KEY_E, - KeyInput.KEY_F, - KeyInput.KEY_G, - KeyInput.KEY_H, - KeyInput.KEY_I, - KeyInput.KEY_J, - KeyInput.KEY_K, - KeyInput.KEY_L, - KeyInput.KEY_M, - KeyInput.KEY_N, - KeyInput.KEY_O, - KeyInput.KEY_P, - KeyInput.KEY_Q, - KeyInput.KEY_R, - KeyInput.KEY_S, - KeyInput.KEY_T, - KeyInput.KEY_U, - KeyInput.KEY_V, - KeyInput.KEY_W, - KeyInput.KEY_X, - KeyInput.KEY_Y, - KeyInput.KEY_Z, - KeyInput.KEY_COMMA, - KeyInput.KEY_PERIOD, - KeyInput.KEY_LMENU, - KeyInput.KEY_RMENU, - KeyInput.KEY_LSHIFT, - KeyInput.KEY_RSHIFT, - // 0x0, // fn - // 0x0, // cap (?) - - KeyInput.KEY_TAB, - KeyInput.KEY_SPACE, - 0x0, // sym (?) symbol - 0x0, // explorer - 0x0, // envelope - KeyInput.KEY_RETURN, // newline/enter - KeyInput.KEY_DELETE, - KeyInput.KEY_GRAVE, - KeyInput.KEY_MINUS, - KeyInput.KEY_EQUALS, - KeyInput.KEY_LBRACKET, - KeyInput.KEY_RBRACKET, - KeyInput.KEY_BACKSLASH, - KeyInput.KEY_SEMICOLON, - KeyInput.KEY_APOSTROPHE, - KeyInput.KEY_SLASH, - KeyInput.KEY_AT, // at (@) - KeyInput.KEY_NUMLOCK, //0x0, // num - 0x0, //headset hook - 0x0, //focus - KeyInput.KEY_ADD, - KeyInput.KEY_LMETA, //menu - 0x0,//notification - 0x0,//search - 0x0,//media play/pause - 0x0,//media stop - 0x0,//media next - 0x0,//media previous - 0x0,//media rewind - 0x0,//media fastforward - 0x0,//mute - }; - - public AndroidInput(Context ctx, AttributeSet attribs) { - super(ctx, attribs); - detector = new GestureDetector(null, this, null, false); - scaledetector = new ScaleGestureDetector(ctx, this); - - } - - public AndroidInput(Context ctx) { - super(ctx); - detector = new GestureDetector(null, this, null, false); - scaledetector = new ScaleGestureDetector(ctx, this); - } - - private TouchEvent getNextFreeTouchEvent() { - return getNextFreeTouchEvent(false); - } - - /** - * Fetches a touch event from the reuse pool - * @param wait if true waits for a reusable event to get available/released by an other thread, if false returns a new one if needed - * @return a usable TouchEvent - */ - private TouchEvent getNextFreeTouchEvent(boolean wait) { - TouchEvent evt = null; - synchronized (eventPoolUnConsumed) { - int size = eventPoolUnConsumed.size(); - while (size > 0) { - evt = eventPoolUnConsumed.pop(); - if (!evt.isConsumed()) { - eventPoolUnConsumed.push(evt); - evt = null; - } else { - break; - } - size--; - } - } - - - if (evt == null) { - if (eventPool.isEmpty() && wait) { - logger.warning("eventPool buffer underrun"); - boolean isEmpty; - do { - synchronized (eventPool) { - isEmpty = eventPool.isEmpty(); - } - try { - Thread.sleep(50); - } catch (InterruptedException e) { - } - } while (isEmpty); - synchronized (eventPool) { - evt = eventPool.pop(); - } - } else if (eventPool.isEmpty()) { - evt = new TouchEvent(); - logger.warning("eventPool buffer underrun"); - } else { - synchronized (eventPool) { - evt = eventPool.pop(); - } - } - } - return evt; - } - - /** - * onTouchEvent gets called from android thread on touchpad events - */ - @Override - public boolean onTouchEvent(MotionEvent event) { - boolean bWasHandled = false; - TouchEvent touch; - // System.out.println("native : " + event.getAction()); - int action = event.getAction() & MotionEvent.ACTION_MASK; - int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) - >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; - int pointerId = event.getPointerId(pointerIndex); - - // final int historySize = event.getHistorySize(); - //final int pointerCount = event.getPointerCount(); - - - switch (action) { - - case MotionEvent.ACTION_POINTER_DOWN: - case MotionEvent.ACTION_DOWN: - - - touch = getNextFreeTouchEvent(); - touch.set(Type.DOWN, event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex), 0, 0); - touch.setPointerId(pointerId); - touch.setTime(event.getEventTime()); - touch.setPressure(event.getPressure(pointerIndex)); - processEvent(touch); - - bWasHandled = true; - break; - - case MotionEvent.ACTION_POINTER_UP: - case MotionEvent.ACTION_CANCEL: - case MotionEvent.ACTION_UP: - - touch = getNextFreeTouchEvent(); - touch.set(Type.UP, event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex), 0, 0); - touch.setPointerId(pointerId); - touch.setTime(event.getEventTime()); - touch.setPressure(event.getPressure(pointerIndex)); - processEvent(touch); - - - bWasHandled = true; - break; - case MotionEvent.ACTION_MOVE: - - - // Convert all pointers into events - for (int p = 0; p < event.getPointerCount(); p++) { - Vector2f lastPos = lastPositions.get(pointerIndex); - if (lastPos == null) { - lastPos = new Vector2f(event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex)); - lastPositions.put(pointerId, lastPos); - } - touch = getNextFreeTouchEvent(); - touch.set(Type.MOVE, event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex), event.getX(pointerIndex) - lastPos.x, this.getHeight() - event.getY(pointerIndex) - lastPos.y); - touch.setPointerId(pointerId); - touch.setTime(event.getEventTime()); - touch.setPressure(event.getPressure(pointerIndex)); - processEvent(touch); - lastPos.set(event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex)); - } - bWasHandled = true; - break; - - - case MotionEvent.ACTION_OUTSIDE: - break; - - } - - // Try to detect gestures  - this.detector.onTouchEvent(event); - this.scaledetector.onTouchEvent(event); - - return bWasHandled; - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - TouchEvent evt; - evt = getNextFreeTouchEvent(); - evt.set(TouchEvent.Type.KEY_DOWN); - evt.setKeyCode(keyCode); - evt.setCharacters(event.getCharacters()); - evt.setTime(event.getEventTime()); - - // Send the event - processEvent(evt); - - // Handle all keys ourself except Volume Up/Down - if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP) || (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) { - return false; - } else { - return true; - } - - } - - @Override - public boolean onKeyUp(int keyCode, KeyEvent event) { - TouchEvent evt; - evt = getNextFreeTouchEvent(); - evt.set(TouchEvent.Type.KEY_UP); - evt.setKeyCode(keyCode); - evt.setCharacters(event.getCharacters()); - evt.setTime(event.getEventTime()); - - // Send the event - processEvent(evt); - - // Handle all keys ourself except Volume Up/Down - if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP) || (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) { - return false; - } else { - return true; - } - } - - // ----------------------------------------- - // JME3 Input interface - @Override - public void initialize() { - TouchEvent item; - for (int i = 0; i < MAX_EVENTS; i++) { - item = new TouchEvent(); - eventPool.push(item); - } - isInitialized = true; - } - - @Override - public void destroy() { - isInitialized = false; - - // Clean up queues - while (!eventPool.isEmpty()) { - eventPool.pop(); - } - while (!eventQueue.isEmpty()) { - eventQueue.pop(); - } - } - - @Override - public boolean isInitialized() { - return isInitialized; - } - - @Override - public void setInputListener(RawInputListener listener) { - this.listener = listener; - } - - @Override - public long getInputTimeNanos() { - return System.nanoTime(); - } - // ----------------------------------------- - - private void processEvent(TouchEvent event) { - synchronized (eventQueue) { - eventQueue.push(event); - } - } - - // --------------- INSIDE GLThread ---------------  - @Override - public void update() { - generateEvents(); - } - - private void generateEvents() { - if (listener != null) { - TouchEvent event; - MouseButtonEvent btn; - int newX; - int newY; - - while (!eventQueue.isEmpty()) { - synchronized (eventQueue) { - event = eventQueue.pop(); - } - if (event != null) { - listener.onTouchEvent(event); - - if (mouseEventsEnabled) { - if (mouseEventsInvertX) { - newX = this.getWidth() - (int) event.getX(); - } else { - newX = (int) event.getX(); - } - - if (mouseEventsInvertY) { - newY = this.getHeight() - (int) event.getY(); - } else { - newY = (int) event.getY(); - } - - switch (event.getType()) { - case DOWN: - // Handle mouse down event  - btn = new MouseButtonEvent(0, true, newX, newY); - btn.setTime(event.getTime()); - listener.onMouseButtonEvent(btn); - // Store current pos - lastX = -1; - lastY = -1; - break; - - case UP: - // Handle mouse up event  - btn = new MouseButtonEvent(0, false, newX, newY); - btn.setTime(event.getTime()); - listener.onMouseButtonEvent(btn); - // Store current pos - lastX = -1; - lastY = -1; - break; - - case MOVE: - int dx; - int dy; - if (lastX != -1) { - dx = newX - lastX; - dy = newY - lastY; - } else { - dx = 0; - dy = 0; - } - MouseMotionEvent mot = new MouseMotionEvent(newX, newY, dx, dy, 0, 0); - mot.setTime(event.getTime()); - listener.onMouseMotionEvent(mot); - lastX = newX; - lastY = newY; - break; - } - - - } - } - - if (event.isConsumed() == false) { - synchronized (eventPoolUnConsumed) { - eventPoolUnConsumed.push(event); - } - - } else { - synchronized (eventPool) { - eventPool.push(event); - } - } - } - - } - } - // --------------- ENDOF INSIDE GLThread ---------------  - - // --------------- Gesture detected callback events ---------------  - public boolean onDown(MotionEvent event) { - return false; - } - - public void onLongPress(MotionEvent event) { - TouchEvent touch = getNextFreeTouchEvent(); - touch.set(Type.LONGPRESSED, event.getX(), this.getHeight() - event.getY(), 0f, 0f); - touch.setPointerId(0); - touch.setTime(event.getEventTime()); - processEvent(touch); - } - - public boolean onFling(MotionEvent event, MotionEvent event2, float vx, float vy) { - TouchEvent touch = getNextFreeTouchEvent(); - touch.set(Type.FLING, event.getX(), this.getHeight() - event.getY(), vx, vy); - touch.setPointerId(0); - touch.setTime(event.getEventTime()); - processEvent(touch); - - return true; - } - - public boolean onSingleTapConfirmed(MotionEvent event) { - //Nothing to do here the tap has already been detected. - return false; - } - - public boolean onDoubleTap(MotionEvent event) { - TouchEvent touch = getNextFreeTouchEvent(); - touch.set(Type.DOUBLETAP, event.getX(), this.getHeight() - event.getY(), 0f, 0f); - touch.setPointerId(0); - touch.setTime(event.getEventTime()); - processEvent(touch); - return true; - } - - public boolean onDoubleTapEvent(MotionEvent event) { - return false; - } - - public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) { - TouchEvent touch = getNextFreeTouchEvent(); - touch.set(Type.SCALE_START, scaleGestureDetector.getFocusX(), scaleGestureDetector.getFocusY(), 0f, 0f); - touch.setPointerId(0); - touch.setTime(scaleGestureDetector.getEventTime()); - touch.setScaleSpan(scaleGestureDetector.getCurrentSpan()); - touch.setScaleFactor(scaleGestureDetector.getScaleFactor()); - processEvent(touch); - // System.out.println("scaleBegin"); - - return true; - } - - public boolean onScale(ScaleGestureDetector scaleGestureDetector) { - TouchEvent touch = getNextFreeTouchEvent(); - touch.set(Type.SCALE_MOVE, scaleGestureDetector.getFocusX(), this.getHeight() - scaleGestureDetector.getFocusY(), 0f, 0f); - touch.setPointerId(0); - touch.setTime(scaleGestureDetector.getEventTime()); - touch.setScaleSpan(scaleGestureDetector.getCurrentSpan()); - touch.setScaleFactor(scaleGestureDetector.getScaleFactor()); - processEvent(touch); - // System.out.println("scale"); - - return false; - } - - public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) { - TouchEvent touch = getNextFreeTouchEvent(); - touch.set(Type.SCALE_END, scaleGestureDetector.getFocusX(), this.getHeight() - scaleGestureDetector.getFocusY(), 0f, 0f); - touch.setPointerId(0); - touch.setTime(scaleGestureDetector.getEventTime()); - touch.setScaleSpan(scaleGestureDetector.getCurrentSpan()); - touch.setScaleFactor(scaleGestureDetector.getScaleFactor()); - processEvent(touch); - } - - public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { - TouchEvent touch = getNextFreeTouchEvent(); - touch.set(Type.SCROLL, e1.getX(), this.getHeight() - e1.getY(), distanceX, distanceY * (-1)); - touch.setPointerId(0); - touch.setTime(e1.getEventTime()); - processEvent(touch); - //System.out.println("scroll " + e1.getPointerCount()); - return false; - } - - public void onShowPress(MotionEvent event) { - TouchEvent touch = getNextFreeTouchEvent(); - touch.set(Type.SHOWPRESS, event.getX(), this.getHeight() - event.getY(), 0f, 0f); - touch.setPointerId(0); - touch.setTime(event.getEventTime()); - processEvent(touch); - } - - public boolean onSingleTapUp(MotionEvent event) { - TouchEvent touch = getNextFreeTouchEvent(); - touch.set(Type.TAP, event.getX(), this.getHeight() - event.getY(), 0f, 0f); - touch.setPointerId(0); - touch.setTime(event.getEventTime()); - processEvent(touch); - return true; - } - - @Override - public void setSimulateMouse(boolean simulate) { - mouseEventsEnabled = simulate; - } - - @Override - public void setSimulateKeyboard(boolean simulate) { - keyboardEventsEnabled = simulate; - } - - @Override - public void setOmitHistoricEvents(boolean dontSendHistory) { - this.dontSendHistory = dontSendHistory; - } - - // TODO: move to TouchInput - public boolean isMouseEventsEnabled() { - return mouseEventsEnabled; - } - - public void setMouseEventsEnabled(boolean mouseEventsEnabled) { - this.mouseEventsEnabled = mouseEventsEnabled; - } - - public boolean isMouseEventsInvertY() { - return mouseEventsInvertY; - } - - public void setMouseEventsInvertY(boolean mouseEventsInvertY) { - this.mouseEventsInvertY = mouseEventsInvertY; - } - - public boolean isMouseEventsInvertX() { - return mouseEventsInvertX; - } - - public void setMouseEventsInvertX(boolean mouseEventsInvertX) { - this.mouseEventsInvertX = mouseEventsInvertX; - } -} +package com.jme3.input.android;  +  +import android.content.Context;  +import android.opengl.GLSurfaceView;  +import android.util.AttributeSet;  +import android.view.GestureDetector;  +import android.view.KeyEvent;  +import android.view.MotionEvent;  +import android.view.ScaleGestureDetector;  +import com.jme3.input.KeyInput;  +import com.jme3.input.RawInputListener;  +import com.jme3.input.TouchInput;  +import com.jme3.input.event.MouseButtonEvent;  +import com.jme3.input.event.MouseMotionEvent;  +import com.jme3.input.event.TouchEvent;  +import com.jme3.input.event.TouchEvent.Type;  +import com.jme3.math.Vector2f;  +import com.jme3.util.RingBuffer;  +import java.util.HashMap;  +import java.util.logging.Logger;  +  +/**  + * <code>AndroidInput</code> is one of the main components that connect jme with android. Is derived from GLSurfaceView and handles all Inputs  + * @author larynx  + *  + */  +public class AndroidInput extends GLSurfaceView implements  + TouchInput,  + GestureDetector.OnGestureListener,  + GestureDetector.OnDoubleTapListener,  + ScaleGestureDetector.OnScaleGestureListener {  +  + final private static int MAX_EVENTS = 1024;  + // Custom settings  + public boolean mouseEventsEnabled = true;  + public boolean mouseEventsInvertX = false;  + public boolean mouseEventsInvertY = false;  + public boolean keyboardEventsEnabled = false;  + public boolean dontSendHistory = false;  + // Used to transfer events from android thread to GLThread  + final private RingBuffer<TouchEvent> eventQueue = new RingBuffer<TouchEvent>(MAX_EVENTS);  + final private RingBuffer<TouchEvent> eventPoolUnConsumed = new RingBuffer<TouchEvent>(MAX_EVENTS);  + final private RingBuffer<TouchEvent> eventPool = new RingBuffer<TouchEvent>(MAX_EVENTS);  + final private HashMap<Integer, Vector2f> lastPositions = new HashMap<Integer, Vector2f>();  + // Internal  + private ScaleGestureDetector scaledetector;  + private GestureDetector detector;  + private int lastX;  + private int lastY;  + private final static Logger logger = Logger.getLogger(AndroidInput.class.getName());  + private boolean isInitialized = false;  + private RawInputListener listener = null;  + private static final int[] ANDROID_TO_JME = {  + 0x0, // unknown  + 0x0, // key code soft left  + 0x0, // key code soft right  + KeyInput.KEY_HOME,  + KeyInput.KEY_ESCAPE, // key back  + 0x0, // key call  + 0x0, // key endcall  + KeyInput.KEY_0,  + KeyInput.KEY_1,  + KeyInput.KEY_2,  + KeyInput.KEY_3,  + KeyInput.KEY_4,  + KeyInput.KEY_5,  + KeyInput.KEY_6,  + KeyInput.KEY_7,  + KeyInput.KEY_8,  + KeyInput.KEY_9,  + KeyInput.KEY_MULTIPLY,  + 0x0, // key pound  + KeyInput.KEY_UP,  + KeyInput.KEY_DOWN,  + KeyInput.KEY_LEFT,  + KeyInput.KEY_RIGHT,  + KeyInput.KEY_RETURN, // dpad center  + 0x0, // volume up  + 0x0, // volume down  + KeyInput.KEY_POWER, // power (?)  + 0x0, // camera  + 0x0, // clear  + KeyInput.KEY_A,  + KeyInput.KEY_B,  + KeyInput.KEY_C,  + KeyInput.KEY_D,  + KeyInput.KEY_E,  + KeyInput.KEY_F,  + KeyInput.KEY_G,  + KeyInput.KEY_H,  + KeyInput.KEY_I,  + KeyInput.KEY_J,  + KeyInput.KEY_K,  + KeyInput.KEY_L,  + KeyInput.KEY_M,  + KeyInput.KEY_N,  + KeyInput.KEY_O,  + KeyInput.KEY_P,  + KeyInput.KEY_Q,  + KeyInput.KEY_R,  + KeyInput.KEY_S,  + KeyInput.KEY_T,  + KeyInput.KEY_U,  + KeyInput.KEY_V,  + KeyInput.KEY_W,  + KeyInput.KEY_X,  + KeyInput.KEY_Y,  + KeyInput.KEY_Z,  + KeyInput.KEY_COMMA,  + KeyInput.KEY_PERIOD,  + KeyInput.KEY_LMENU,  + KeyInput.KEY_RMENU,  + KeyInput.KEY_LSHIFT,  + KeyInput.KEY_RSHIFT,  + // 0x0, // fn  + // 0x0, // cap (?)  +  + KeyInput.KEY_TAB,  + KeyInput.KEY_SPACE,  + 0x0, // sym (?) symbol  + 0x0, // explorer  + 0x0, // envelope  + KeyInput.KEY_RETURN, // newline/enter  + KeyInput.KEY_DELETE,  + KeyInput.KEY_GRAVE,  + KeyInput.KEY_MINUS,  + KeyInput.KEY_EQUALS,  + KeyInput.KEY_LBRACKET,  + KeyInput.KEY_RBRACKET,  + KeyInput.KEY_BACKSLASH,  + KeyInput.KEY_SEMICOLON,  + KeyInput.KEY_APOSTROPHE,  + KeyInput.KEY_SLASH,  + KeyInput.KEY_AT, // at (@)  + KeyInput.KEY_NUMLOCK, //0x0, // num  + 0x0, //headset hook  + 0x0, //focus  + KeyInput.KEY_ADD,  + KeyInput.KEY_LMETA, //menu  + 0x0,//notification  + 0x0,//search  + 0x0,//media play/pause  + 0x0,//media stop  + 0x0,//media next  + 0x0,//media previous  + 0x0,//media rewind  + 0x0,//media fastforward  + 0x0,//mute  + };  +  + public AndroidInput(Context ctx, AttributeSet attribs) {  + super(ctx, attribs);  + detector = new GestureDetector(null, this, null, false);  + scaledetector = new ScaleGestureDetector(ctx, this);  +  + }  +  + public AndroidInput(Context ctx) {  + super(ctx);  + detector = new GestureDetector(null, this, null, false);  + scaledetector = new ScaleGestureDetector(ctx, this);  + }  +  + private TouchEvent getNextFreeTouchEvent() {  + return getNextFreeTouchEvent(false);  + }  +  + /**  + * Fetches a touch event from the reuse pool  + * @param wait if true waits for a reusable event to get available/released  + * by an other thread, if false returns a new one if needed.  + *  + * @return a usable TouchEvent  + */  + private TouchEvent getNextFreeTouchEvent(boolean wait) {  + TouchEvent evt = null;  + synchronized (eventPoolUnConsumed) {  + int size = eventPoolUnConsumed.size();  + while (size > 0) {  + evt = eventPoolUnConsumed.pop();  + if (!evt.isConsumed()) {  + eventPoolUnConsumed.push(evt);  + evt = null;  + } else {  + break;  + }  + size--;  + }  + }  +  + if (evt == null) {  + if (eventPool.isEmpty() && wait) {  + logger.warning("eventPool buffer underrun");  + boolean isEmpty;  + do {  + synchronized (eventPool) {  + isEmpty = eventPool.isEmpty();  + }  + try {  + Thread.sleep(50);  + } catch (InterruptedException e) {  + }  + } while (isEmpty);  + synchronized (eventPool) {  + evt = eventPool.pop();  + }  + } else if (eventPool.isEmpty()) {  + evt = new TouchEvent();  + logger.warning("eventPool buffer underrun");  + } else {  + synchronized (eventPool) {  + evt = eventPool.pop();  + }  + }  + }  + return evt;  + }  +  + /**  + * onTouchEvent gets called from android thread on touchpad events  + */  + @Override  + public boolean onTouchEvent(MotionEvent event) {  + boolean bWasHandled = false;  + TouchEvent touch;  + // System.out.println("native : " + event.getAction());  + int action = event.getAction() & MotionEvent.ACTION_MASK;  + int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)  + >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;  + int pointerId = event.getPointerId(pointerIndex);  +  + // final int historySize = event.getHistorySize();  + //final int pointerCount = event.getPointerCount();  +  + switch (action) {  + case MotionEvent.ACTION_POINTER_DOWN:  + case MotionEvent.ACTION_DOWN:  + touch = getNextFreeTouchEvent();  + touch.set(Type.DOWN, event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex), 0, 0);  + touch.setPointerId(pointerId);  + touch.setTime(event.getEventTime());  + touch.setPressure(event.getPressure(pointerIndex));  + processEvent(touch);  +  + bWasHandled = true;  + break;  +  + case MotionEvent.ACTION_POINTER_UP:  + case MotionEvent.ACTION_CANCEL:  + case MotionEvent.ACTION_UP:  +  + touch = getNextFreeTouchEvent();  + touch.set(Type.UP, event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex), 0, 0);  + touch.setPointerId(pointerId);  + touch.setTime(event.getEventTime());  + touch.setPressure(event.getPressure(pointerIndex));  + processEvent(touch);  +  +  + bWasHandled = true;  + break;  + case MotionEvent.ACTION_MOVE:  +  +  + // Convert all pointers into events  + for (int p = 0; p < event.getPointerCount(); p++) {  + Vector2f lastPos = lastPositions.get(pointerIndex);  + if (lastPos == null) {  + lastPos = new Vector2f(event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex));  + lastPositions.put(pointerId, lastPos);  + }  + touch = getNextFreeTouchEvent();  + touch.set(Type.MOVE, event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex), event.getX(pointerIndex) - lastPos.x, this.getHeight() - event.getY(pointerIndex) - lastPos.y);  + touch.setPointerId(pointerId);  + touch.setTime(event.getEventTime());  + touch.setPressure(event.getPressure(pointerIndex));  + processEvent(touch);  + lastPos.set(event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex));  + }  + bWasHandled = true;  + break;  + case MotionEvent.ACTION_OUTSIDE:  + break;  +  + }  +  + // Try to detect gestures  + this.detector.onTouchEvent(event);  + this.scaledetector.onTouchEvent(event);  +  + return bWasHandled;  + }  +  + @Override  + public boolean onKeyDown(int keyCode, KeyEvent event) {  + TouchEvent evt;  + evt = getNextFreeTouchEvent();  + evt.set(TouchEvent.Type.KEY_DOWN);  + evt.setKeyCode(keyCode);  + evt.setCharacters(event.getCharacters());  + evt.setTime(event.getEventTime());  +  + // Send the event  + processEvent(evt);  +  + // Handle all keys ourself except Volume Up/Down  + if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP) || (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) {  + return false;  + } else {  + return true;  + }  + }  +  + @Override  + public boolean onKeyUp(int keyCode, KeyEvent event) {  + TouchEvent evt;  + evt = getNextFreeTouchEvent();  + evt.set(TouchEvent.Type.KEY_UP);  + evt.setKeyCode(keyCode);  + evt.setCharacters(event.getCharacters());  + evt.setTime(event.getEventTime());  +  + // Send the event  + processEvent(evt);  +  + // Handle all keys ourself except Volume Up/Down  + if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP) || (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) {  + return false;  + } else {  + return true;  + }  + }  +  + // -----------------------------------------  + // JME3 Input interface  + @Override  + public void initialize() {  + TouchEvent item;  + for (int i = 0; i < MAX_EVENTS; i++) {  + item = new TouchEvent();  + eventPool.push(item);  + }  + isInitialized = true;  + }  +  + @Override  + public void destroy() {  + isInitialized = false;  +  + // Clean up queues  + while (!eventPool.isEmpty()) {  + eventPool.pop();  + }  + while (!eventQueue.isEmpty()) {  + eventQueue.pop();  + }  + }  +  + @Override  + public boolean isInitialized() {  + return isInitialized;  + }  +  + @Override  + public void setInputListener(RawInputListener listener) {  + this.listener = listener;  + }  +  + @Override  + public long getInputTimeNanos() {  + return System.nanoTime();  + }  + // -----------------------------------------  +  + private void processEvent(TouchEvent event) {  + synchronized (eventQueue) {  + eventQueue.push(event);  + }  + }  +  + // --------------- INSIDE GLThread ---------------  + @Override  + public void update() {  + generateEvents();  + }  +  + private void generateEvents() {  + if (listener != null) {  + TouchEvent event;  + MouseButtonEvent btn;  + int newX;  + int newY;  +  + while (!eventQueue.isEmpty()) {  + synchronized (eventQueue) {  + event = eventQueue.pop();  + }  + if (event != null) {  + listener.onTouchEvent(event);  +  + if (mouseEventsEnabled) {  + if (mouseEventsInvertX) {  + newX = this.getWidth() - (int) event.getX();  + } else {  + newX = (int) event.getX();  + }  +  + if (mouseEventsInvertY) {  + newY = this.getHeight() - (int) event.getY();  + } else {  + newY = (int) event.getY();  + }  +  + switch (event.getType()) {  + case DOWN:  + // Handle mouse down event  + btn = new MouseButtonEvent(0, true, newX, newY);  + btn.setTime(event.getTime());  + listener.onMouseButtonEvent(btn);  + // Store current pos  + lastX = -1;  + lastY = -1;  + break;  +  + case UP:  + // Handle mouse up event  + btn = new MouseButtonEvent(0, false, newX, newY);  + btn.setTime(event.getTime());  + listener.onMouseButtonEvent(btn);  + // Store current pos  + lastX = -1;  + lastY = -1;  + break;  +  + case MOVE:  + int dx;  + int dy;  + if (lastX != -1) {  + dx = newX - lastX;  + dy = newY - lastY;  + } else {  + dx = 0;  + dy = 0;  + }  + MouseMotionEvent mot = new MouseMotionEvent(newX, newY, dx, dy, 0, 0);  + mot.setTime(event.getTime());  + listener.onMouseMotionEvent(mot);  + lastX = newX;  + lastY = newY;  + break;  + }  +  +  + }  + }  +  + if (event.isConsumed() == false) {  + synchronized (eventPoolUnConsumed) {  + eventPoolUnConsumed.push(event);  + }  +  + } else {  + synchronized (eventPool) {  + eventPool.push(event);  + }  + }  + }  +  + }  + }  + // --------------- ENDOF INSIDE GLThread ---------------  +  + // --------------- Gesture detected callback events ---------------  + public boolean onDown(MotionEvent event) {  + return false;  + }  +  + public void onLongPress(MotionEvent event) {  + TouchEvent touch = getNextFreeTouchEvent();  + touch.set(Type.LONGPRESSED, event.getX(), this.getHeight() - event.getY(), 0f, 0f);  + touch.setPointerId(0);  + touch.setTime(event.getEventTime());  + processEvent(touch);  + }  +  + public boolean onFling(MotionEvent event, MotionEvent event2, float vx, float vy) {  + TouchEvent touch = getNextFreeTouchEvent();  + touch.set(Type.FLING, event.getX(), this.getHeight() - event.getY(), vx, vy);  + touch.setPointerId(0);  + touch.setTime(event.getEventTime());  + processEvent(touch);  +  + return true;  + }  +  + public boolean onSingleTapConfirmed(MotionEvent event) {  + //Nothing to do here the tap has already been detected.  + return false;  + }  +  + public boolean onDoubleTap(MotionEvent event) {  + TouchEvent touch = getNextFreeTouchEvent();  + touch.set(Type.DOUBLETAP, event.getX(), this.getHeight() - event.getY(), 0f, 0f);  + touch.setPointerId(0);  + touch.setTime(event.getEventTime());  + processEvent(touch);  + return true;  + }  +  + public boolean onDoubleTapEvent(MotionEvent event) {  + return false;  + }  +  + public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {  + TouchEvent touch = getNextFreeTouchEvent();  + touch.set(Type.SCALE_START, scaleGestureDetector.getFocusX(), scaleGestureDetector.getFocusY(), 0f, 0f);  + touch.setPointerId(0);  + touch.setTime(scaleGestureDetector.getEventTime());  + touch.setScaleSpan(scaleGestureDetector.getCurrentSpan());  + touch.setScaleFactor(scaleGestureDetector.getScaleFactor());  + processEvent(touch);  + // System.out.println("scaleBegin");  +  + return true;  + }  +  + public boolean onScale(ScaleGestureDetector scaleGestureDetector) {  + TouchEvent touch = getNextFreeTouchEvent();  + touch.set(Type.SCALE_MOVE, scaleGestureDetector.getFocusX(), this.getHeight() - scaleGestureDetector.getFocusY(), 0f, 0f);  + touch.setPointerId(0);  + touch.setTime(scaleGestureDetector.getEventTime());  + touch.setScaleSpan(scaleGestureDetector.getCurrentSpan());  + touch.setScaleFactor(scaleGestureDetector.getScaleFactor());  + processEvent(touch);  + // System.out.println("scale");  +  + return false;  + }  +  + public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {  + TouchEvent touch = getNextFreeTouchEvent();  + touch.set(Type.SCALE_END, scaleGestureDetector.getFocusX(), this.getHeight() - scaleGestureDetector.getFocusY(), 0f, 0f);  + touch.setPointerId(0);  + touch.setTime(scaleGestureDetector.getEventTime());  + touch.setScaleSpan(scaleGestureDetector.getCurrentSpan());  + touch.setScaleFactor(scaleGestureDetector.getScaleFactor());  + processEvent(touch);  + }  +  + public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {  + TouchEvent touch = getNextFreeTouchEvent();  + touch.set(Type.SCROLL, e1.getX(), this.getHeight() - e1.getY(), distanceX, distanceY * (-1));  + touch.setPointerId(0);  + touch.setTime(e1.getEventTime());  + processEvent(touch);  + //System.out.println("scroll " + e1.getPointerCount());  + return false;  + }  +  + public void onShowPress(MotionEvent event) {  + TouchEvent touch = getNextFreeTouchEvent();  + touch.set(Type.SHOWPRESS, event.getX(), this.getHeight() - event.getY(), 0f, 0f);  + touch.setPointerId(0);  + touch.setTime(event.getEventTime());  + processEvent(touch);  + }  +  + public boolean onSingleTapUp(MotionEvent event) {  + TouchEvent touch = getNextFreeTouchEvent();  + touch.set(Type.TAP, event.getX(), this.getHeight() - event.getY(), 0f, 0f);  + touch.setPointerId(0);  + touch.setTime(event.getEventTime());  + processEvent(touch);  + return true;  + }  +  + @Override  + public void setSimulateMouse(boolean simulate) {  + mouseEventsEnabled = simulate;  + }  + @Override  + public boolean getSimulateMouse() {  + return mouseEventsEnabled;  + }  +  + @Override  + public void setSimulateKeyboard(boolean simulate) {  + keyboardEventsEnabled = simulate;  + }  +  + @Override  + public void setOmitHistoricEvents(boolean dontSendHistory) {  + this.dontSendHistory = dontSendHistory;  + }  +  + // TODO: move to TouchInput  + public boolean isMouseEventsEnabled() {  + return mouseEventsEnabled;  + }  +  + public void setMouseEventsEnabled(boolean mouseEventsEnabled) {  + this.mouseEventsEnabled = mouseEventsEnabled;  + }  +  + public boolean isMouseEventsInvertY() {  + return mouseEventsInvertY;  + }  +  + public void setMouseEventsInvertY(boolean mouseEventsInvertY) {  + this.mouseEventsInvertY = mouseEventsInvertY;  + }  +  + public boolean isMouseEventsInvertX() {  + return mouseEventsInvertX;  + }  +  + public void setMouseEventsInvertX(boolean mouseEventsInvertX) {  + this.mouseEventsInvertX = mouseEventsInvertX;  + }  +}  
diff --git a/engine/src/android/com/jme3/input/android/AndroidTouchInputListener.java b/engine/src/android/com/jme3/input/android/AndroidTouchInputListener.java index 34e4592..c8b2228 100644 --- a/engine/src/android/com/jme3/input/android/AndroidTouchInputListener.java +++ b/engine/src/android/com/jme3/input/android/AndroidTouchInputListener.java 
@@ -6,14 +6,17 @@  import com.jme3.input.event.TouchEvent;    /** - * AndroidTouchInputListener is an inputlistener interface which defines callbacks/events for android touch screens - * For use with class AndroidInput + * AndroidTouchInputListener is an inputlistener interface which defines + * callbacks/events for android touch screens For use with class AndroidInput + *  * @author larynx  *  */ -public interface AndroidTouchInputListener extends RawInputListener -{ +public interface AndroidTouchInputListener extends RawInputListener { +  public void onTouchEvent(TouchEvent evt); +  public void onMotionEvent(MotionEvent evt); +  public void onAndroidKeyEvent(KeyEvent evt);  } 
diff --git a/engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java b/engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java index b5a8c14..7e7af29 100644 --- a/engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java +++ b/engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java 
@@ -31,9 +31,7 @@  */  package com.jme3.renderer.android;   -import android.graphics.Bitmap;  import android.opengl.GLES10; -import android.opengl.GLES11;  import android.opengl.GLES20;  import android.os.Build;  import com.jme3.asset.AndroidImageInfo; @@ -60,14 +58,11 @@  import com.jme3.util.BufferUtils;  import com.jme3.util.ListMap;  import com.jme3.util.NativeObjectManager; -import com.jme3.util.SafeArrayList;  import java.nio.*; -import java.util.ArrayList;  import java.util.EnumSet;  import java.util.List;  import java.util.logging.Level;  import java.util.logging.Logger; -import javax.microedition.khronos.opengles.GL10;    public class OGLESShaderRenderer implements Renderer {   @@ -360,25 +355,10 @@  }    applyRenderState(RenderState.DEFAULT); -// GLES20.glClearDepthf(1.0f); - - if (verboseLogging) { - logger.info("GLES20.glDisable(GL10.GL_DITHER)"); - } - - GLES20.glDisable(GL10.GL_DITHER); + GLES20.glDisable(GLES20.GL_DITHER);    checkGLError();   - if (verboseLogging) { - logger.info("GLES20.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST)"); - } -  - //It seems that GL10.GL_PERSPECTIVE_CORRECTION_HINT gives invalid_enum error on android.  -// GLES20.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST); - -//	checkGLError(); -  useVBO = false;    // NOTE: SDK_INT is only available since 1.6,  @@ -1779,10 +1759,8 @@    private int convertWrapMode(Texture.WrapMode mode) {  switch (mode) { -// case BorderClamp: -// return GLES20.GL_CLAMP_TO_BORDER; -// case Clamp: -// return GLES20.GL_CLAMP; + case BorderClamp: + case Clamp:  case EdgeClamp:  return GLES20.GL_CLAMP_TO_EDGE;  case Repeat: @@ -2557,7 +2535,7 @@  logger.log(Level.INFO, "glDrawElements(), indexBuf.capacity ({0}), vertCount ({1})", new Object[]{indexBuf.getData().capacity(), vertCount});  }   - GLES11.glDrawElements( + GLES20.glDrawElements(  convertElementMode(mesh.getMode()),  indexBuf.getData().capacity(),  convertFormat(indexBuf.getFormat()), 
diff --git a/engine/src/android/com/jme3/renderer/android/TextureUtil.java b/engine/src/android/com/jme3/renderer/android/TextureUtil.java index 53b96b4..4a60d6b 100644 --- a/engine/src/android/com/jme3/renderer/android/TextureUtil.java +++ b/engine/src/android/com/jme3/renderer/android/TextureUtil.java 
@@ -70,7 +70,7 @@  width /= 2;  Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, width, height, true);   - bitmap.recycle(); + //bitmap.recycle();  bitmap = bitmap2;  }  } @@ -94,7 +94,7 @@  width = FastMath.nearestPowerOfTwo(width);  height = FastMath.nearestPowerOfTwo(height);  Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, width, height, true); - bitmap.recycle(); + //bitmap.recycle();  bitmap = bitmap2;  }  } 
diff --git a/engine/src/android/com/jme3/system/android/AndroidConfigChooser.java b/engine/src/android/com/jme3/system/android/AndroidConfigChooser.java index e55fa55..56ed415 100644 --- a/engine/src/android/com/jme3/system/android/AndroidConfigChooser.java +++ b/engine/src/android/com/jme3/system/android/AndroidConfigChooser.java 
@@ -2,6 +2,7 @@    import android.graphics.PixelFormat;  import android.opengl.GLSurfaceView.EGLConfigChooser; +import java.util.logging.Level;  import java.util.logging.Logger;  import javax.microedition.khronos.egl.EGL10;  import javax.microedition.khronos.egl.EGLConfig; @@ -9,26 +10,23 @@    /**  * AndroidConfigChooser is used to determine the best suited EGL Config - * @author larynx  * + * @author larynx  */ -public class AndroidConfigChooser implements EGLConfigChooser  -{ +public class AndroidConfigChooser implements EGLConfigChooser { +  private static final Logger logger = Logger.getLogger(AndroidConfigChooser.class.getName()); -   protected int clientOpenGLESVersion = 0;  protected EGLConfig bestConfig = null;  protected EGLConfig fastestConfig = null;  protected EGLConfig choosenConfig = null;  protected ConfigType type;  protected int pixelFormat; -   protected boolean verbose = false; -   private final static int EGL_OPENGL_ES2_BIT = 4;   - public enum ConfigType  - { + public enum ConfigType { +  /**  * RGB565, 0 alpha, 16 depth, 0 stencil  */ @@ -39,94 +37,79 @@  BEST,  /**  * Turn off config chooser and use hardcoded - * setEGLContextClientVersion(2); - * setEGLConfigChooser(5, 6, 5, 0, 16, 0); + * setEGLContextClientVersion(2); setEGLConfigChooser(5, 6, 5, 0, 16, + * 0);  */  LEGACY  } -  - public AndroidConfigChooser(ConfigType type, boolean verbose) - { + + public AndroidConfigChooser(ConfigType type) {  this.type = type; - this.verbose = verbose;  } -  +  /**  * Gets called by the GLSurfaceView class to return the best config - */  + */  @Override - public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) - { + public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {  logger.info("GLSurfaceView asks for egl config, returning: ");  logEGLConfig(choosenConfig, display, egl);  return choosenConfig;  } -  +  /**  * findConfig is used to locate the best config and init the chooser with + *  * @param egl  * @param display  * @return true if successfull, false if no config was found  */ - public boolean findConfig(EGL10 egl, EGLDisplay display) - {  -  - if (type == ConfigType.BEST) - {  -	ComponentSizeChooser compChooser = new ComponentSizeChooser(8, 8, 8, 8, 32, 0); -	choosenConfig = compChooser.chooseConfig(egl, display); + public boolean findConfig(EGL10 egl, EGLDisplay display) {   -	if (choosenConfig == null) -	{ + if (type == ConfigType.BEST) { + ComponentSizeChooser compChooser = new ComponentSizeChooser(8, 8, 8, 8, 32, 0); + choosenConfig = compChooser.chooseConfig(egl, display); + + if (choosenConfig == null) {  compChooser = new ComponentSizeChooser(8, 8, 8, 0, 32, 0);  choosenConfig = compChooser.chooseConfig(egl, display); - if (choosenConfig == null) - { + if (choosenConfig == null) {  compChooser = new ComponentSizeChooser(8, 8, 8, 8, 16, 0);  choosenConfig = compChooser.chooseConfig(egl, display); - if (choosenConfig == null) - { + if (choosenConfig == null) {  compChooser = new ComponentSizeChooser(8, 8, 8, 0, 16, 0);  choosenConfig = compChooser.chooseConfig(egl, display);  }  } -	} -  + } +  logger.info("JME3 using best EGL configuration available here: "); - } - else - { -	ComponentSizeChooser compChooser = new ComponentSizeChooser(5, 6, 5, 0, 16, 0); -	choosenConfig = compChooser.chooseConfig(egl, display); + } else { + ComponentSizeChooser compChooser = new ComponentSizeChooser(5, 6, 5, 0, 16, 0); + choosenConfig = compChooser.chooseConfig(egl, display);  logger.info("JME3 using fastest EGL configuration available here: ");  } -  - if (choosenConfig != null) - { - logger.info("JME3 using choosen config: ");  - logEGLConfig(choosenConfig, display, egl);  + + if (choosenConfig != null) { + logger.info("JME3 using choosen config: "); + logEGLConfig(choosenConfig, display, egl);  pixelFormat = getPixelFormat(choosenConfig, display, egl);  clientOpenGLESVersion = getOpenGLVersion(choosenConfig, display, egl);  return true; - } - else - { + } else {  logger.severe("###ERROR### Unable to get a valid OpenGL ES 2.0 config, nether Fastest nor Best found! Bug. Please report this.");  clientOpenGLESVersion = 1;  pixelFormat = PixelFormat.UNKNOWN;  return false; - }  + }  } -  -  - private int getPixelFormat(EGLConfig conf, EGLDisplay display, EGL10 egl) - { + + private int getPixelFormat(EGLConfig conf, EGLDisplay display, EGL10 egl) {  int[] value = new int[1]; - int result = PixelFormat.RGB_565;  + int result = PixelFormat.RGB_565;    egl.eglGetConfigAttrib(display, conf, EGL10.EGL_RED_SIZE, value); - if (value[0] == 8) - { + if (value[0] == 8) {  result = PixelFormat.RGBA_8888;  /*  egl.eglGetConfigAttrib(display, conf, EGL10.EGL_ALPHA_SIZE, value); @@ -139,165 +122,163 @@  result = PixelFormat.RGB_888;  }*/  } -  - if (verbose) - { - logger.info("Using PixelFormat " + result);  + + if (verbose) { + logger.log(Level.INFO, "Using PixelFormat {0}", result);  } -  +  //return result; TODO Test pixelformat  return PixelFormat.TRANSPARENT;  } -  - private int getOpenGLVersion(EGLConfig conf, EGLDisplay display, EGL10 egl) - { + + private int getOpenGLVersion(EGLConfig conf, EGLDisplay display, EGL10 egl) {  int[] value = new int[1];  int result = 1; -  +  egl.eglGetConfigAttrib(display, conf, EGL10.EGL_RENDERABLE_TYPE, value);  // Check if conf is OpenGL ES 2.0 - if ((value[0] & EGL_OPENGL_ES2_BIT) != 0) - { + if ((value[0] & EGL_OPENGL_ES2_BIT) != 0) {  result = 2;  }   - return result;  + return result;  } -  +  /**  * log output with egl config details + *  * @param conf  * @param display  * @param egl  */ - public void logEGLConfig(EGLConfig conf, EGLDisplay display, EGL10 egl) - { + public void logEGLConfig(EGLConfig conf, EGLDisplay display, EGL10 egl) {  int[] value = new int[1];    egl.eglGetConfigAttrib(display, conf, EGL10.EGL_RED_SIZE, value); - logger.info(String.format("EGL_RED_SIZE = %d", value[0] ) ); -  + logger.info(String.format("EGL_RED_SIZE = %d", value[0])); +  egl.eglGetConfigAttrib(display, conf, EGL10.EGL_GREEN_SIZE, value); - logger.info(String.format("EGL_GREEN_SIZE = %d", value[0] ) ); -  + logger.info(String.format("EGL_GREEN_SIZE = %d", value[0])); +  egl.eglGetConfigAttrib(display, conf, EGL10.EGL_BLUE_SIZE, value); - logger.info(String.format("EGL_BLUE_SIZE = %d", value[0] ) ); -  + logger.info(String.format("EGL_BLUE_SIZE = %d", value[0])); +  egl.eglGetConfigAttrib(display, conf, EGL10.EGL_ALPHA_SIZE, value); - logger.info(String.format("EGL_ALPHA_SIZE = %d", value[0] ) ); -  + logger.info(String.format("EGL_ALPHA_SIZE = %d", value[0])); +  egl.eglGetConfigAttrib(display, conf, EGL10.EGL_DEPTH_SIZE, value); - logger.info(String.format("EGL_DEPTH_SIZE = %d", value[0] ) ); -  + logger.info(String.format("EGL_DEPTH_SIZE = %d", value[0])); +  egl.eglGetConfigAttrib(display, conf, EGL10.EGL_STENCIL_SIZE, value); - logger.info(String.format("EGL_STENCIL_SIZE = %d", value[0] ) ); + logger.info(String.format("EGL_STENCIL_SIZE = %d", value[0]));    egl.eglGetConfigAttrib(display, conf, EGL10.EGL_RENDERABLE_TYPE, value); - logger.info(String.format("EGL_RENDERABLE_TYPE = %d", value[0] ) ); -  + logger.info(String.format("EGL_RENDERABLE_TYPE = %d", value[0])); +  egl.eglGetConfigAttrib(display, conf, EGL10.EGL_SURFACE_TYPE, value); - logger.info(String.format("EGL_SURFACE_TYPE = %d", value[0] ) );  + logger.info(String.format("EGL_SURFACE_TYPE = %d", value[0]));  } -  - public int getClientOpenGLESVersion()  - { + + public int getClientOpenGLESVersion() {  return clientOpenGLESVersion;  }   - public void setClientOpenGLESVersion(int clientOpenGLESVersion)  - { + public void setClientOpenGLESVersion(int clientOpenGLESVersion) {  this.clientOpenGLESVersion = clientOpenGLESVersion;  } -  - public int getPixelFormat()  - { + + public int getPixelFormat() {  return pixelFormat;  } -  -  -  - private abstract class BaseConfigChooser implements EGLConfigChooser  - { + + private abstract class BaseConfigChooser implements EGLConfigChooser { +  private boolean bClientOpenGLESVersionSet; -  -	public BaseConfigChooser(int[] configSpec)  -	{ - bClientOpenGLESVersionSet = false; - mConfigSpec = filterConfigSpec(configSpec); -	} - -	public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display)  -	{ - int[] num_config = new int[1]; - if (!egl.eglChooseConfig(display, mConfigSpec, null, 0, - num_config)) { - throw new IllegalArgumentException("eglChooseConfig failed"); - } - - int numConfigs = num_config[0]; - - if (numConfigs <= 0)  - { - //throw new IllegalArgumentException("No configs match configSpec"); -  - return null; - } - - EGLConfig[] configs = new EGLConfig[numConfigs]; - if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs, - num_config)) { - throw new IllegalArgumentException("eglChooseConfig#2 failed"); - } - EGLConfig config = chooseConfig(egl, display, configs); - //if (config == null) { - // throw new IllegalArgumentException("No config chosen"); - //} - return config; -	} - -	abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, - EGLConfig[] configs); - -	protected int[] mConfigSpec; - -	private int[] filterConfigSpec(int[] configSpec)  -	{ - if (bClientOpenGLESVersionSet == true) { - return configSpec; - } - /* We know none of the subclasses define EGL_RENDERABLE_TYPE. - * And we know the configSpec is well formed. - */ - int len = configSpec.length; - int[] newConfigSpec = new int[len + 2]; - System.arraycopy(configSpec, 0, newConfigSpec, 0, len-1); - newConfigSpec[len-1] = EGL10.EGL_RENDERABLE_TYPE; - newConfigSpec[len] = 4; /* EGL_OPENGL_ES2_BIT */ - newConfigSpec[len+1] = EGL10.EGL_NONE; -  - bClientOpenGLESVersionSet = true; -  - return newConfigSpec; -	} + + public BaseConfigChooser(int[] configSpec) { + bClientOpenGLESVersionSet = false; + mConfigSpec = filterConfigSpec(configSpec); + } + + public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { + int[] num_config = new int[1]; + if (!egl.eglChooseConfig(display, mConfigSpec, null, 0, + num_config)) { + throw new IllegalArgumentException("eglChooseConfig failed"); + } + + int numConfigs = num_config[0]; + + if (numConfigs <= 0) { + //throw new IllegalArgumentException("No configs match configSpec"); + + return null; + } + + EGLConfig[] configs = new EGLConfig[numConfigs]; + if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs, + num_config)) { + throw new IllegalArgumentException("eglChooseConfig#2 failed"); + } + EGLConfig config = chooseConfig(egl, display, configs); + //if (config == null) { + // throw new IllegalArgumentException("No config chosen"); + //} + return config; + } + + abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, + EGLConfig[] configs); + protected int[] mConfigSpec; + + private int[] filterConfigSpec(int[] configSpec) { + if (bClientOpenGLESVersionSet == true) { + return configSpec; + } + /* + * We know none of the subclasses define EGL_RENDERABLE_TYPE. And we + * know the configSpec is well formed. + */ + int len = configSpec.length; + int[] newConfigSpec = new int[len + 2]; + System.arraycopy(configSpec, 0, newConfigSpec, 0, len - 1); + newConfigSpec[len - 1] = EGL10.EGL_RENDERABLE_TYPE; + newConfigSpec[len] = 4; /* + * EGL_OPENGL_ES2_BIT + */ + newConfigSpec[len + 1] = EGL10.EGL_NONE; + + bClientOpenGLESVersionSet = true; + + return newConfigSpec; + }  } -  +  /** - * Choose a configuration with exactly the specified r,g,b,a sizes, - * and at least the specified depth and stencil sizes. + * Choose a configuration with exactly the specified r,g,b,a sizes, and at + * least the specified depth and stencil sizes.  */ - private class ComponentSizeChooser extends BaseConfigChooser  - { + private class ComponentSizeChooser extends BaseConfigChooser { + + private int[] mValue; + // Subclasses can adjust these values: + protected int mRedSize; + protected int mGreenSize; + protected int mBlueSize; + protected int mAlphaSize; + protected int mDepthSize; + protected int mStencilSize; +   public ComponentSizeChooser(int redSize, int greenSize, int blueSize, - int alphaSize, int depthSize, int stencilSize)  - { - super(new int[] { - EGL10.EGL_RED_SIZE, redSize, - EGL10.EGL_GREEN_SIZE, greenSize, - EGL10.EGL_BLUE_SIZE, blueSize, - EGL10.EGL_ALPHA_SIZE, alphaSize, - EGL10.EGL_DEPTH_SIZE, depthSize, - EGL10.EGL_STENCIL_SIZE, stencilSize, - EGL10.EGL_NONE}); + int alphaSize, int depthSize, int stencilSize) { + super(new int[]{ + EGL10.EGL_RED_SIZE, redSize, + EGL10.EGL_GREEN_SIZE, greenSize, + EGL10.EGL_BLUE_SIZE, blueSize, + EGL10.EGL_ALPHA_SIZE, alphaSize, + EGL10.EGL_DEPTH_SIZE, depthSize, + EGL10.EGL_STENCIL_SIZE, stencilSize, + EGL10.EGL_NONE});  mValue = new int[1];  mRedSize = redSize;  mGreenSize = greenSize; @@ -305,25 +286,22 @@  mAlphaSize = alphaSize;  mDepthSize = depthSize;  mStencilSize = stencilSize; - } + }    @Override - public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs)  - { - for (EGLConfig config : configs)  - { + public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs) { + for (EGLConfig config : configs) {  int d = findConfigAttrib(egl, display, config,  EGL10.EGL_DEPTH_SIZE, 0);  int s = findConfigAttrib(egl, display, config,  EGL10.EGL_STENCIL_SIZE, 0); - if ((d >= mDepthSize) && (s >= mStencilSize))  - { + if ((d >= mDepthSize) && (s >= mStencilSize)) {  int r = findConfigAttrib(egl, display, config,  EGL10.EGL_RED_SIZE, 0);  int g = findConfigAttrib(egl, display, config, - EGL10.EGL_GREEN_SIZE, 0); + EGL10.EGL_GREEN_SIZE, 0);  int b = findConfigAttrib(egl, display, config, - EGL10.EGL_BLUE_SIZE, 0); + EGL10.EGL_BLUE_SIZE, 0);  int a = findConfigAttrib(egl, display, config,  EGL10.EGL_ALPHA_SIZE, 0);  if ((r == mRedSize) && (g == mGreenSize) @@ -336,27 +314,12 @@  }    private int findConfigAttrib(EGL10 egl, EGLDisplay display, - EGLConfig config, int attribute, int defaultValue)  - { + EGLConfig config, int attribute, int defaultValue) {   - if (egl.eglGetConfigAttrib(display, config, attribute, mValue))  - { + if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {  return mValue[0];  }  return defaultValue;  } - - private int[] mValue; - // Subclasses can adjust these values: - protected int mRedSize; - protected int mGreenSize; - protected int mBlueSize; - protected int mAlphaSize; - protected int mDepthSize; - protected int mStencilSize;  } -  -  -  -   } 
diff --git a/engine/src/android/com/jme3/system/android/OGLESContext.java b/engine/src/android/com/jme3/system/android/OGLESContext.java index 668b68d..1ecf2e1 100644 --- a/engine/src/android/com/jme3/system/android/OGLESContext.java +++ b/engine/src/android/com/jme3/system/android/OGLESContext.java 
@@ -32,26 +32,37 @@  package com.jme3.system.android;    import android.app.Activity; +import android.app.AlertDialog;  import android.content.Context; +import android.content.DialogInterface;  import android.opengl.GLSurfaceView; +import android.text.InputType; +import android.view.Gravity;  import android.view.SurfaceHolder; +import android.view.ViewGroup.LayoutParams; +import android.widget.EditText; +import android.widget.FrameLayout;  import com.jme3.app.AndroidHarness;  import com.jme3.app.Application;  import com.jme3.input.JoyInput;  import com.jme3.input.KeyInput;  import com.jme3.input.MouseInput; +import com.jme3.input.SoftTextDialogInput;  import com.jme3.input.TouchInput;  import com.jme3.input.android.AndroidInput; +import com.jme3.input.controls.SoftTextDialogInputListener;  import com.jme3.input.controls.TouchTrigger;  import com.jme3.input.dummy.DummyKeyInput;  import com.jme3.input.dummy.DummyMouseInput;  import com.jme3.renderer.android.OGLESShaderRenderer;  import com.jme3.system.AppSettings;  import com.jme3.system.JmeContext; +import com.jme3.system.JmeSystem;  import com.jme3.system.SystemListener;  import com.jme3.system.Timer;  import com.jme3.system.android.AndroidConfigChooser.ConfigType;  import java.util.concurrent.atomic.AtomicBoolean; +import java.util.logging.Level;  import java.util.logging.Logger;  import javax.microedition.khronos.egl.EGL10;  import javax.microedition.khronos.egl.EGLConfig; @@ -59,7 +70,7 @@  import javax.microedition.khronos.egl.EGLDisplay;  import javax.microedition.khronos.opengles.GL10;   -public class OGLESContext implements JmeContext, GLSurfaceView.Renderer { +public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTextDialogInput {    private static final Logger logger = Logger.getLogger(OGLESContext.class.getName());  protected final AtomicBoolean created = new AtomicBoolean(false); @@ -76,7 +87,6 @@  protected boolean autoFlush = true;  protected AndroidInput view;  private boolean firstDrawFrame = true; -  //protected int minFrameDuration = 1000 / frameRate; // Set a max FPS of 33  protected int minFrameDuration = 0; // No FPS cap  /** @@ -145,12 +155,12 @@    int[] version = new int[2];  if (egl.eglInitialize(display, version) == true) { - logger.info("Display EGL Version: " + version[0] + "." + version[1]); + logger.log(Level.INFO, "Display EGL Version: {0}.{1}", new Object[]{version[0], version[1]});  }    try {  // Create a config chooser - AndroidConfigChooser configChooser = new AndroidConfigChooser(configType, eglConfigVerboseLogging); + AndroidConfigChooser configChooser = new AndroidConfigChooser(configType);  // Init chooser  if (!configChooser.findConfig(egl, display)) {  listener.handleError("Unable to find suitable EGL config", null); @@ -162,7 +172,7 @@  listener.handleError("OpenGL ES 2.0 is not supported on this device", null);  return null;  } -  +  // Requesting client version from GLSurfaceView which is extended by  // AndroidInput.  view.setEGLContextClientVersion(clientOpenGLESVersion); @@ -210,12 +220,14 @@  // Setup unhandled Exception Handler  if (ctx instanceof AndroidHarness) {  Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { +  public void uncaughtException(Thread thread, Throwable thrown) {  ((AndroidHarness) ctx).handleError("Exception thrown in " + thread.toString(), thrown);  }  });  } else {  Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { +  public void uncaughtException(Thread thread, Throwable thrown) {  listener.handleError("Exception thrown in " + thread.toString(), thrown);  } @@ -244,6 +256,8 @@  }  }   + JmeSystem.setSoftTextDialogInput(this); +  needClose.set(false);  renderable.set(true);  } @@ -268,6 +282,13 @@  logger.info("Display destroyed.");    renderable.set(false); + final Context ctx = this.view.getContext(); + if (ctx instanceof AndroidHarness) { + AndroidHarness harness = (AndroidHarness) ctx; + if (harness.isFinishOnAppStop()) { + harness.finish(); + } + }  }  }   @@ -442,4 +463,67 @@  public int getClientOpenGLESVersion() {  return clientOpenGLESVersion;  } + + public void requestDialog(final int id, final String title, final String initialValue, final SoftTextDialogInputListener listener) { + logger.log(Level.INFO, "requestDialog: title: {0}, initialValue: {1}", + new Object[]{title, initialValue}); + + JmeAndroidSystem.getActivity().runOnUiThread(new Runnable() { + + @Override + public void run() { + + final FrameLayout layoutTextDialogInput = new FrameLayout(JmeAndroidSystem.getActivity()); + final EditText editTextDialogInput = new EditText(JmeAndroidSystem.getActivity()); + editTextDialogInput.setWidth(LayoutParams.FILL_PARENT); + editTextDialogInput.setHeight(LayoutParams.FILL_PARENT); + editTextDialogInput.setPadding(20, 20, 20, 20); + editTextDialogInput.setGravity(Gravity.FILL_HORIZONTAL); + + editTextDialogInput.setText(initialValue); + + switch (id) { + case SoftTextDialogInput.TEXT_ENTRY_DIALOG: + + editTextDialogInput.setInputType(InputType.TYPE_CLASS_TEXT); + break; + + case SoftTextDialogInput.NUMERIC_ENTRY_DIALOG: + + editTextDialogInput.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL | InputType.TYPE_NUMBER_FLAG_SIGNED); + break; + + case SoftTextDialogInput.NUMERIC_KEYPAD_DIALOG: + + editTextDialogInput.setInputType(InputType.TYPE_CLASS_PHONE); + break; + + default: + break; + } + + layoutTextDialogInput.addView(editTextDialogInput); + + AlertDialog dialogTextInput = new AlertDialog.Builder(JmeAndroidSystem.getActivity()).setTitle(title).setView(layoutTextDialogInput).setPositiveButton("OK", + new DialogInterface.OnClickListener() { + + public void onClick(DialogInterface dialog, int whichButton) { + /* User clicked OK, send COMPLETE action + * and text */ + listener.onSoftText(SoftTextDialogInputListener.COMPLETE, editTextDialogInput.getText().toString()); + } + }).setNegativeButton("Cancel", + new DialogInterface.OnClickListener() { + + public void onClick(DialogInterface dialog, int whichButton) { + /* User clicked CANCEL, send CANCEL action + * and text */ + listener.onSoftText(SoftTextDialogInputListener.CANCEL, editTextDialogInput.getText().toString()); + } + }).create(); + + dialogTextInput.show(); + } + }); + }  } 
diff --git a/engine/src/android/com/jme3/util/AndroidLogHandler.java b/engine/src/android/com/jme3/util/AndroidLogHandler.java index 8fb21c2..1da1299 100644 --- a/engine/src/android/com/jme3/util/AndroidLogHandler.java +++ b/engine/src/android/com/jme3/util/AndroidLogHandler.java 
@@ -4,7 +4,12 @@  import java.util.logging.Handler;  import java.util.logging.Level;  import java.util.logging.LogRecord; +import java.util.logging.Logger;   +/** + * Converts from Java based logging ({@link Logger} to Android based + * logging {@link Log}. + */  public class AndroidLogHandler extends Handler {    @Override @@ -21,17 +26,16 @@  String clsName = record.getSourceClassName();  String msg = record.getMessage();  Throwable t = record.getThrown(); - if (level == Level.INFO){ + if (level == Level.INFO) {  Log.i(clsName, msg, t); - }else if (level == Level.SEVERE){ + } else if (level == Level.SEVERE) {  Log.e(clsName, msg, t); - }else if (level == Level.WARNING){ + } else if (level == Level.WARNING) {  Log.w(clsName, msg, t); - }else if (level == Level.CONFIG){ + } else if (level == Level.CONFIG) {  Log.d(clsName, msg, t); - }else if (level == Level.FINE || level == Level.FINER || level == Level.FINEST){ + } else if (level == Level.FINE || level == Level.FINER || level == Level.FINEST) {  Log.v(clsName, msg, t);  }  } -  } 
diff --git a/engine/src/android/com/jme3/util/RingBuffer.java b/engine/src/android/com/jme3/util/RingBuffer.java index 786417b..1d3c22d 100644 --- a/engine/src/android/com/jme3/util/RingBuffer.java +++ b/engine/src/android/com/jme3/util/RingBuffer.java 
@@ -4,20 +4,21 @@  import java.util.NoSuchElementException;    /** - * Ring buffer (fixed size queue) implementation using a circular array (array with wrap-around). + * Ring buffer (fixed size queue) implementation using a circular array (array + * with wrap-around).  */  // suppress unchecked warnings in Java 1.5.0_6 and later  @SuppressWarnings("unchecked") -public class RingBuffer<Item> implements Iterable<Item> { +public class RingBuffer<T> implements Iterable<T> {   - private Item[] buffer; // queue elements + private T[] buffer; // queue elements  private int count = 0; // number of elements on queue  private int indexOut = 0; // index of first element of queue  private int indexIn = 0; // index of next available slot    // cast needed since no generic array creation in Java  public RingBuffer(int capacity) { - buffer = (Item[]) new Object[capacity]; + buffer = (T[]) new Object[capacity];  }    public boolean isEmpty() { @@ -28,7 +29,7 @@  return count;  }   - public void push(Item item) { + public void push(T item) {  if (count == buffer.length) {  throw new RuntimeException("Ring buffer overflow");  } @@ -37,23 +38,23 @@  count++;  }   - public Item pop() { + public T pop() {  if (isEmpty()) {  throw new RuntimeException("Ring buffer underflow");  } - Item item = buffer[indexOut]; + T item = buffer[indexOut];  buffer[indexOut] = null; // to help with garbage collection  count--;  indexOut = (indexOut + 1) % buffer.length; // wrap-around  return item;  }   - public Iterator<Item> iterator() { + public Iterator<T> iterator() {  return new RingBufferIterator();  }    // an iterator, doesn't implement remove() since it's optional - private class RingBufferIterator implements Iterator<Item> { + private class RingBufferIterator implements Iterator<T> {    private int i = 0;   @@ -65,7 +66,7 @@  throw new UnsupportedOperationException();  }   - public Item next() { + public T next() {  if (!hasNext()) {  throw new NoSuchElementException();  } 
diff --git a/engine/src/android/res/layout/about.xml b/engine/src/android/res/layout/about.xml index 1d8c2b0..ca249e7 100644 --- a/engine/src/android/res/layout/about.xml +++ b/engine/src/android/res/layout/about.xml 
@@ -1,29 +1,29 @@  <?xml version="1.0" encoding="utf-8"?>    <LinearLayout -	xmlns:android="http://schemas.android.com/apk/res/android" -	android:orientation="vertical" -	android:layout_width="fill_parent" -	android:layout_height="fill_parent" + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent"  >    <LinearLayout -	android:id="@+id/buttonsContainer" -	xmlns:android="http://schemas.android.com/apk/res/android" -	android:orientation="vertical" -	android:layout_width="fill_parent" -	android:layout_height="fill_parent" -	> + android:id="@+id/buttonsContainer" + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + >  <TextView  -	android:layout_width="fill_parent"  -	android:layout_height="wrap_content"  -	android:text="copyright (c) 2009-2010 JMonkeyEngine" + android:layout_width="fill_parent"  + android:layout_height="wrap_content"  + android:text="copyright (c) 2009-2010 JMonkeyEngine" 	/>    <TextView  -	android:layout_width="fill_parent"  -	android:layout_height="wrap_content"  -	android:text="http://www.jmonkeyengine.org" + android:layout_width="fill_parent"  + android:layout_height="wrap_content"  + android:text="http://www.jmonkeyengine.org" 	/>    </LinearLayout> 
diff --git a/engine/src/android/res/layout/tests.xml b/engine/src/android/res/layout/tests.xml index 70bd5ec..f38e233 100644 --- a/engine/src/android/res/layout/tests.xml +++ b/engine/src/android/res/layout/tests.xml 
@@ -1,17 +1,17 @@  <?xml version="1.0" encoding="utf-8"?>    <LinearLayout -	xmlns:android="http://schemas.android.com/apk/res/android" -	android:orientation="vertical" -	android:layout_width="fill_parent" -	android:layout_height="fill_parent" + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent"  >  <LinearLayout -	android:id="@+id/buttonsContainer" -	xmlns:android="http://schemas.android.com/apk/res/android" -	android:orientation="vertical" -	android:layout_width="fill_parent" -	android:layout_height="fill_parent"> + android:id="@+id/buttonsContainer" + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent">  <!-- 	<Button 	android:id="@+id/SimpleTextured" 
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshHelper.java index 484e942..7f9e8ac 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshHelper.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshHelper.java 
@@ -31,6 +31,14 @@  */   package com.jme3.scene.plugins.blender.meshes;    +import java.nio.ByteBuffer;  +import java.util.ArrayList;  +import java.util.HashMap;  +import java.util.LinkedList;  +import java.util.List;  +import java.util.Map;  +import java.util.Map.Entry;  +   import com.jme3.asset.BlenderKey.FeaturesToLoad;   import com.jme3.material.Material;   import com.jme3.math.FastMath;  @@ -57,10 +65,6 @@  import com.jme3.scene.plugins.blender.textures.UVCoordinatesGenerator;   import com.jme3.texture.Texture;   import com.jme3.util.BufferUtils;  -import java.nio.ByteBuffer;  -import java.nio.FloatBuffer;  -import java.util.*;  -import java.util.Map.Entry;     /**   * A class that is used in mesh calculations.  @@ -300,12 +304,13 @@  // generating meshes   //FloatBuffer verticesColorsBuffer = this.createFloatBuffer(verticesColors);   ByteBuffer verticesColorsBuffer = createByteBuffer(verticesColors);  + verticesAmount = vertexList.size();   for (Entry<Integer, List<Integer>> meshEntry : meshesMap.entrySet()) {   Mesh mesh = new Mesh();     // creating vertices indices for this mesh   List<Integer> indexList = meshEntry.getValue();  - if(verticesAmount < Short.MAX_VALUE * 2) {  + if(verticesAmount <= Short.MAX_VALUE) {  	short[] indices = new short[indexList.size()];   for (int i = 0; i < indexList.size(); ++i) {   indices[i] = indexList.get(i).shortValue();  
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java index 75fc0c5..918072e 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java 
@@ -56,8 +56,8 @@ 	float[] resultPixel = new float[4];  	int dataIndex = 0;  	while (data.hasRemaining()) {  -	float tin = this.setupMaterialColor(data, format, neg, pixelColor);  -	this.blendPixel(resultPixel, materialColor, color, tin, blendType, blenderContext);  +	this.setupMaterialColor(data, format, neg, pixelColor);  +	this.blendPixel(resultPixel, materialColor, pixelColor, affectFactor, blendType, blenderContext);  	newData.put(dataIndex++, (byte) (resultPixel[0] * 255.0f));  	newData.put(dataIndex++, (byte) (resultPixel[1] * 255.0f));  	newData.put(dataIndex++, (byte) (resultPixel[2] * 255.0f));  
diff --git a/engine/src/core-data/Common/MatDefs/Light/Lighting.frag b/engine/src/core-data/Common/MatDefs/Light/Lighting.frag index 76f0e15..eb1c3fd 100644 --- a/engine/src/core-data/Common/MatDefs/Light/Lighting.frag +++ b/engine/src/core-data/Common/MatDefs/Light/Lighting.frag 
@@ -126,6 +126,10 @@  float att = vLightDir.w;   #endif    + if (m_Shininess <= 1.0) {  + specularFactor = 0.0; // should be one instruction on most cards ..  + }  +   specularFactor *= diffuseFactor;     return vec2(diffuseFactor, specularFactor) * vec2(att);  
diff --git a/engine/src/core-data/Common/MatDefs/Misc/Particle.j3md b/engine/src/core-data/Common/MatDefs/Misc/Particle.j3md index e28d41a..73674f9 100644 --- a/engine/src/core-data/Common/MatDefs/Misc/Particle.j3md +++ b/engine/src/core-data/Common/MatDefs/Misc/Particle.j3md 
@@ -4,6 +4,10 @@  Texture2D Texture   Float Quadratic   Boolean PointSprite  +  + //only used for soft particles  + Texture2D DepthTexture  + Float Softness     // Texture of the glowing parts of the material   Texture2D GlowMap  @@ -58,6 +62,28 @@  }   }    + Technique SoftParticles{  +  + VertexShader GLSL100 : Common/MatDefs/Misc/SoftParticle.vert  + FragmentShader GLSL100 : Common/MatDefs/Misc/SoftParticle.frag  +  + WorldParameters {  + WorldViewProjectionMatrix  + WorldViewMatrix  + WorldMatrix  + CameraPosition  + }  +  + RenderState {  + Blend AlphaAdditive  + DepthWrite Off  + }  +  + Defines {  + USE_TEXTURE : Texture  + }  + }  +   Technique FixedFunc {   RenderState {   Blend AlphaAdditive  
diff --git a/engine/src/core-data/Common/MatDefs/Misc/SoftParticle.frag b/engine/src/core-data/Common/MatDefs/Misc/SoftParticle.frag new file mode 100644 index 0000000..d3108b5 --- /dev/null +++ b/engine/src/core-data/Common/MatDefs/Misc/SoftParticle.frag 
@@ -0,0 +1,50 @@ +uniform sampler2D m_DepthTexture;  +uniform float m_Softness; // Power used in the contrast function  +varying vec2 vPos; // Position of the pixel  +varying vec2 projPos;// z and w valus in projection space  +  +#ifdef USE_TEXTURE  +uniform sampler2D m_Texture;  +varying vec4 texCoord;  +#endif  +  +varying vec4 color;  +  +float Contrast(float d){  + float val = clamp( 2.0*( (d > 0.5) ? 1.0-d : d ), 0.0, 1.0);  + float a = 0.5 * pow(val, m_Softness);  + return (d > 0.5) ? 1.0 - a : a;  +}  +  +float stdDiff(float d){  + return clamp((d)*m_Softness,0.0,1.0);  +}  +  +  +void main(){  + if (color.a <= 0.01)  + discard;  +  + vec4 c = vec4(1.0,1.0,1.0,1.0);//color;  + #ifdef USE_TEXTURE  + #ifdef POINT_SPRITE  + vec2 uv = mix(texCoord.xy, texCoord.zw, gl_PointCoord.xy);  + #else  + vec2 uv = texCoord.xy;  + #endif  + c = texture2D(m_Texture, uv) * color;  + #endif  +  +  + float depthv = texture2D(m_DepthTexture, vPos).x*2.0-1.0; // Scene depth  + depthv*=projPos.y;  + float particleDepth = projPos.x;  +  + float zdiff =depthv-particleDepth;  + if(zdiff<=0.0){  + discard;  + }  + // Computes alpha based on the particles distance to the rest of the scene  + c.a = c.a * stdDiff(zdiff);// Contrast(zdiff);  + gl_FragColor =c;  +} \ No newline at end of file 
diff --git a/engine/src/core-data/Common/MatDefs/Misc/SoftParticle.vert b/engine/src/core-data/Common/MatDefs/Misc/SoftParticle.vert new file mode 100644 index 0000000..4f0dfd5 --- /dev/null +++ b/engine/src/core-data/Common/MatDefs/Misc/SoftParticle.vert 
@@ -0,0 +1,53 @@ +uniform mat4 g_WorldViewProjectionMatrix;  +  +attribute vec3 inPosition;  +attribute vec4 inColor;  +attribute vec4 inTexCoord;  +  +varying vec4 color;  +// z and w values in projection space  +varying vec2 projPos;  +varying vec2 vPos; // Position of the pixel in clip space  +  +  +  +#ifdef USE_TEXTURE  +varying vec4 texCoord;  +#endif  +  +#ifdef POINT_SPRITE  +uniform mat4 g_WorldViewMatrix;  +uniform mat4 g_WorldMatrix;  +uniform vec3 g_CameraPosition;  +uniform float m_Quadratic;  +const float SIZE_MULTIPLIER = 4.0;  +attribute float inSize;  +#endif  +  +void main(){  + vec4 pos = vec4(inPosition, 1.0);  +  + gl_Position = g_WorldViewProjectionMatrix * pos;  + color = inColor;  +  + projPos = gl_Position.zw;  + // projPos.x = 0.5 * (projPos.x) + 0.5;  +  + // Transforms the vPosition data to the range [0,1]  + vPos = (gl_Position.xy / gl_Position.w + 1.0) / 2.0;  +  + #ifdef USE_TEXTURE  + texCoord = inTexCoord;  + #endif  +  + #ifdef POINT_SPRITE  + vec4 worldPos = g_WorldMatrix * pos;  + float d = distance(g_CameraPosition.xyz, worldPos.xyz);  + gl_PointSize = max(1.0, (inSize * SIZE_MULTIPLIER * m_Quadratic) / d);  +  + //vec4 worldViewPos = g_WorldViewMatrix * pos;  + //gl_PointSize = (inSize * SIZE_MULTIPLIER * m_Quadratic)*100.0 / worldViewPos.z;  +  + color.a *= min(gl_PointSize, 1.0);  + #endif  +} \ No newline at end of file 
diff --git a/engine/src/core-effects/com/jme3/post/filters/TranslucentBucketFilter.java b/engine/src/core-effects/com/jme3/post/filters/TranslucentBucketFilter.java index 47be413..c377a58 100644 --- a/engine/src/core-effects/com/jme3/post/filters/TranslucentBucketFilter.java +++ b/engine/src/core-effects/com/jme3/post/filters/TranslucentBucketFilter.java 
@@ -5,6 +5,7 @@  package com.jme3.post.filters;    import com.jme3.asset.AssetManager; +import com.jme3.effect.ParticleEmitter;  import com.jme3.material.Material;  import com.jme3.math.ColorRGBA;  import com.jme3.post.Filter; @@ -12,8 +13,14 @@  import com.jme3.renderer.Renderer;  import com.jme3.renderer.ViewPort;  import com.jme3.renderer.queue.RenderQueue; +import com.jme3.scene.Node; + +import com.jme3.scene.Spatial;  import com.jme3.texture.FrameBuffer; +import com.jme3.texture.Texture;  import com.jme3.texture.Texture2D; +import java.util.logging.Level; +import java.util.logging.Logger;    /**  * A filter to handle translucent objects when rendering a scene with filters that uses depth like WaterFilter and SSAOFilter @@ -22,11 +29,25 @@  */  public final class TranslucentBucketFilter extends Filter {   + private final static Logger logger = Logger.getLogger(TranslucentBucketFilter.class.getName());  private RenderManager renderManager; + private boolean enabledSoftParticles = false; + private Texture depthTexture; + private ViewPort viewPort; + + public TranslucentBucketFilter() { + super("TranslucentBucketFilter"); + } + + public TranslucentBucketFilter(boolean enabledSoftParticles) { + this(); + this.enabledSoftParticles = enabledSoftParticles; + }    @Override  protected void initFilter(AssetManager manager, RenderManager rm, ViewPort vp, int w, int h) {  this.renderManager = rm; + this.viewPort = vp;  material = new Material(manager, "Common/MatDefs/Post/Overlay.j3md");  material.setColor("Color", ColorRGBA.White);  Texture2D tex = processor.getFilterTexture(); @@ -37,6 +58,26 @@  material.clearParam("NumSamples");  }  renderManager.setHandleTranslucentBucket(false); + if (enabledSoftParticles && depthTexture != null) { + initSoftParticles(vp, true); + } + } + + private void initSoftParticles(ViewPort vp, boolean enabledSP) { + if (depthTexture != null) { + for (Spatial scene : vp.getScenes()) { + makeSoftParticleEmitter(scene, enabledSP && enabled); + } + } + + } + + @Override + protected void setDepthTexture(Texture depthTexture) { + this.depthTexture = depthTexture; + if (enabledSoftParticles && depthTexture != null) { + initSoftParticles(viewPort, true); + }  }    /** @@ -49,6 +90,11 @@  }    @Override + protected boolean isRequiresDepthTexture() { + return enabledSoftParticles; + } + + @Override  protected void postFrame(RenderManager renderManager, ViewPort viewPort, FrameBuffer prevFilterBuffer, FrameBuffer sceneBuffer) {  renderManager.setCamera(viewPort.getCamera(), false);  if (prevFilterBuffer != sceneBuffer) { @@ -63,6 +109,8 @@  if (renderManager != null) {  renderManager.setHandleTranslucentBucket(true);  } + + initSoftParticles(viewPort, false);  }    @Override @@ -76,5 +124,32 @@  if (renderManager != null) {  renderManager.setHandleTranslucentBucket(!enabled);  } + initSoftParticles(viewPort, enabledSoftParticles); + } + + private void makeSoftParticleEmitter(Spatial scene, boolean enabled) { + if (scene instanceof Node) { + Node n = (Node) scene; + for (Spatial child : n.getChildren()) { + makeSoftParticleEmitter(child, enabled); + } + } + if (scene instanceof ParticleEmitter) { + ParticleEmitter emitter = (ParticleEmitter) scene; + if (enabled) { + enabledSoftParticles = enabled; + + emitter.getMaterial().selectTechnique("SoftParticles", renderManager); + emitter.getMaterial().setTexture("DepthTexture", processor.getDepthTexture()); + emitter.setQueueBucket(RenderQueue.Bucket.Translucent); + + logger.log(Level.INFO, "Made particle Emitter {0} soft.", emitter.getName()); + } else { + emitter.getMaterial().clearParam("DepthTexture"); + emitter.getMaterial().selectTechnique("Default", renderManager); + // emitter.setQueueBucket(RenderQueue.Bucket.Transparent); + logger.log(Level.INFO, "Particle Emitter {0} is not soft anymore.", emitter.getName()); + } + }  }  } 
diff --git a/engine/src/core-plugins/com/jme3/asset/plugins/HttpZipLocator.java~ b/engine/src/core-plugins/com/jme3/asset/plugins/HttpZipLocator.java~ deleted file mode 100644 index f5fbfd1..0000000 --- a/engine/src/core-plugins/com/jme3/asset/plugins/HttpZipLocator.java~ +++ /dev/null 
@@ -1,355 +0,0 @@ -/* - * Copyright (c) 2009-2010 jMonkeyEngine - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of 'jMonkeyEngine' nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.jme3.asset.plugins; - -import com.jme3.asset.AssetInfo; -import com.jme3.asset.AssetKey; -import com.jme3.asset.AssetLocator; -import com.jme3.asset.AssetManager; -import java.io.IOException; -import java.io.InputStream; -import java.net.HttpURLConnection; -import java.net.URL; -import java.nio.ByteBuffer; -import java.nio.CharBuffer; -import java.nio.charset.CharacterCodingException; -import java.nio.charset.Charset; -import java.nio.charset.CharsetDecoder; -import java.nio.charset.CoderResult; -import java.util.HashMap; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.zip.Inflater; -import java.util.zip.InflaterInputStream; -import java.util.zip.ZipEntry; - -public class HttpZipLocator implements AssetLocator { - - private static final Logger logger = Logger.getLogger(HttpZipLocator.class.getName()); - - private URL zipUrl; - private String rootPath = ""; - private int numEntries; - private int tableOffset; - private int tableLength; - private HashMap<String, ZipEntry2> entries; -  - private static final ByteBuffer byteBuf = ByteBuffer.allocate(250); - private static final CharBuffer charBuf = CharBuffer.allocate(250); - private static final CharsetDecoder utf8Decoder; -  - static { - Charset utf8 = Charset.forName("UTF-8"); - utf8Decoder = utf8.newDecoder(); - } - - private static class ZipEntry2 { - String name; - int length; - int offset; - int compSize; - long crc; - boolean deflate; - - @Override - public String toString(){ - return "ZipEntry[name=" + name + - ", length=" + length + - ", compSize=" + compSize + - ", offset=" + offset + "]"; - } - } - - private static int get16(byte[] b, int off) { -	return (b[off++] & 0xff) | - ((b[off] & 0xff) << 8); - } - - private static int get32(byte[] b, int off) { -	return (b[off++] & 0xff) | - ((b[off++] & 0xff) << 8) | - ((b[off++] & 0xff) << 16) | - ((b[off] & 0xff) << 24); - } - - private static long getu32(byte[] b, int off) throws IOException{ - return (b[off++]&0xff) | - ((b[off++]&0xff) << 8) | - ((b[off++]&0xff) << 16) | - (((long)(b[off]&0xff)) << 24); - } - - private static String getUTF8String(byte[] b, int off, int len) throws CharacterCodingException { - StringBuilder sb = new StringBuilder(); -  - int read = 0; - while (read < len){ - // Either read n remaining bytes in b or 250 if n is higher. - int toRead = Math.min(len - read, byteBuf.capacity()); -  - boolean endOfInput = toRead < byteBuf.capacity(); -  - // read 'toRead' bytes into byteBuf - byteBuf.put(b, off + read, toRead); -  - // set limit to position and set position to 0 - // so data can be decoded - byteBuf.flip(); -  - // decode data in byteBuf - CoderResult result = utf8Decoder.decode(byteBuf, charBuf, endOfInput);  -  - // if the result is not an underflow its an error - // that cannot be handled. - // if the error is an underflow and its the end of input - // then the decoder expects more bytes but there are no more => error - if (!result.isUnderflow() || !endOfInput){ - result.throwException(); - } -  - // flip the char buf to get the string just decoded - charBuf.flip(); -  - // append the decoded data into the StringBuilder - sb.append(charBuf.toString()); -  - // clear buffers for next use - byteBuf.clear(); - charBuf.clear(); -  - read += toRead; - } -  - return sb.toString(); - } - - private InputStream readData(int offset, int length) throws IOException{ - HttpURLConnection conn = (HttpURLConnection) zipUrl.openConnection(); - conn.setDoOutput(false); - conn.setUseCaches(false); - conn.setInstanceFollowRedirects(false); - String range = "-"; - if (offset != Integer.MAX_VALUE){ - range = offset + range; - } - if (length != Integer.MAX_VALUE){ - if (offset != Integer.MAX_VALUE){ - range = range + (offset + length - 1); - }else{ - range = range + length; - } - } - - conn.setRequestProperty("Range", "bytes=" + range); - conn.connect(); - if (conn.getResponseCode() == HttpURLConnection.HTTP_PARTIAL){ - return conn.getInputStream(); - }else if (conn.getResponseCode() == HttpURLConnection.HTTP_OK){ - throw new IOException("Your server does not support HTTP feature Content-Range. Please contact your server administrator."); - }else{ - throw new IOException(conn.getResponseCode() + " " + conn.getResponseMessage()); - } - } - - private int readTableEntry(byte[] table, int offset) throws IOException{ - if (get32(table, offset) != ZipEntry.CENSIG){ - throw new IOException("Central directory error, expected 'PK12'"); - } - - int nameLen = get16(table, offset + ZipEntry.CENNAM); - int extraLen = get16(table, offset + ZipEntry.CENEXT); - int commentLen = get16(table, offset + ZipEntry.CENCOM); - int newOffset = offset + ZipEntry.CENHDR + nameLen + extraLen + commentLen; - - int flags = get16(table, offset + ZipEntry.CENFLG); - if ((flags & 1) == 1){ - // ignore this entry, it uses encryption - return newOffset; - } -  - int method = get16(table, offset + ZipEntry.CENHOW); - if (method != ZipEntry.DEFLATED && method != ZipEntry.STORED){ - // ignore this entry, it uses unknown compression method - return newOffset; - } - - String name = getUTF8String(table, offset + ZipEntry.CENHDR, nameLen); - if (name.charAt(name.length()-1) == '/'){ - // ignore this entry, it is directory node - // or it has no name (?) - return newOffset; - } - - ZipEntry2 entry = new ZipEntry2(); - entry.name = name; - entry.deflate = (method == ZipEntry.DEFLATED); - entry.crc = getu32(table, offset + ZipEntry.CENCRC); - entry.length = get32(table, offset + ZipEntry.CENLEN); - entry.compSize = get32(table, offset + ZipEntry.CENSIZ); - entry.offset = get32(table, offset + ZipEntry.CENOFF); - - // we want offset directly into file data .. - // move the offset forward to skip the LOC header - entry.offset += ZipEntry.LOCHDR + nameLen + extraLen; - - entries.put(entry.name, entry); -  - return newOffset; - } - - private void fillByteArray(byte[] array, InputStream source) throws IOException{ - int total = 0; - int length = array.length; -	while (total < length) { - int read = source.read(array, total, length - total); - if (read < 0) - throw new IOException("Failed to read entire array"); - - total += read; -	} - } - - private void readCentralDirectory() throws IOException{ - InputStream in = readData(tableOffset, tableLength); - byte[] header = new byte[tableLength]; - - // Fix for "PK12 bug in town.zip": sometimes - // not entire byte array will be read with InputStream.read() - // (especially for big headers) - fillByteArray(header, in); - -// in.read(header); - in.close(); - - entries = new HashMap<String, ZipEntry2>(numEntries); - int offset = 0; - for (int i = 0; i < numEntries; i++){ - offset = readTableEntry(header, offset); - } - } - - private void readEndHeader() throws IOException{ - -// InputStream in = readData(Integer.MAX_VALUE, ZipEntry.ENDHDR); -// byte[] header = new byte[ZipEntry.ENDHDR]; -// fillByteArray(header, in); -// in.close(); -// -// if (get32(header, 0) != ZipEntry.ENDSIG){ -// throw new IOException("End header error, expected 'PK56'"); -// } - - // Fix for "PK56 bug in town.zip": - // If there's a zip comment inside the end header, - // PK56 won't appear in the -22 position relative to the end of the - // file! - // In that case, we have to search for it. - // Increase search space to 200 bytes - - InputStream in = readData(Integer.MAX_VALUE, 200); - byte[] header = new byte[200]; - fillByteArray(header, in); - in.close(); - - int offset = -1; - for (int i = 200 - 22; i >= 0; i--){ - if (header[i] == (byte) (ZipEntry.ENDSIG & 0xff) - && get32(header, i) == ZipEntry.ENDSIG){ - // found location - offset = i; - break; - } - } - if (offset == -1) - throw new IOException("Cannot find Zip End Header in file!"); - - numEntries = get16(header, offset + ZipEntry.ENDTOT); - tableLength = get32(header, offset + ZipEntry.ENDSIZ); - tableOffset = get32(header, offset + ZipEntry.ENDOFF); - } - - public void load(URL url) throws IOException { - if (!url.getProtocol().equals("http")) - throw new UnsupportedOperationException(); - - zipUrl = url; - readEndHeader(); - readCentralDirectory(); - } - - private InputStream openStream(ZipEntry2 entry) throws IOException{ - InputStream in = readData(entry.offset, entry.compSize); - if (entry.deflate){ - return new InflaterInputStream(in, new Inflater(true)); - } - return in; - } - - public InputStream openStream(String name) throws IOException{ - ZipEntry2 entry = entries.get(name); - if (entry == null) - throw new RuntimeException("Entry not found: "+name); - - return openStream(entry); - } - - public void setRootPath(String path){ - if (!rootPath.equals(path)){ - rootPath = path; - try { - load(new URL(path)); - } catch (IOException ex) { - logger.log(Level.WARNING, "Failed to set root path "+path, ex); - } - } - } - - public AssetInfo locate(AssetManager manager, AssetKey key){ - final ZipEntry2 entry = entries.get(key.getName()); - if (entry == null) - return null; - - return new AssetInfo(manager, key){ - @Override - public InputStream openStream() { - try { - return HttpZipLocator.this.openStream(entry); - } catch (IOException ex) { - logger.log(Level.WARNING, "Error retrieving "+entry.name, ex); - return null; - } - } - }; - } - -} 
diff --git a/engine/src/core-plugins/com/jme3/scene/plugins/OBJLoader.java b/engine/src/core-plugins/com/jme3/scene/plugins/OBJLoader.java index 3ce7f52..47165f6 100644 --- a/engine/src/core-plugins/com/jme3/scene/plugins/OBJLoader.java +++ b/engine/src/core-plugins/com/jme3/scene/plugins/OBJLoader.java 
@@ -454,9 +454,11 @@    if (hasNormals){  normBuf = BufferUtils.createFloatBuffer(vertIndexMap.size() * 3); + m.setBuffer(VertexBuffer.Type.Normal, 3, normBuf);  }  if (hasTexCoord){  tcBuf = BufferUtils.createFloatBuffer(vertIndexMap.size() * 2); + m.setBuffer(VertexBuffer.Type.TexCoord, 2, tcBuf);  }    IndexBuffer indexBuf = null; @@ -517,9 +519,7 @@  }    m.setBuffer(VertexBuffer.Type.Position, 3, posBuf); - m.setBuffer(VertexBuffer.Type.Normal, 3, normBuf); - m.setBuffer(VertexBuffer.Type.TexCoord, 2, tcBuf); - // index buffer was set on creation + // index buffer and others were set on creation    m.setStatic();  m.updateBound(); 
diff --git a/engine/src/core/com/jme3/app/Application.java b/engine/src/core/com/jme3/app/Application.java index 517ec61..5100725 100644 --- a/engine/src/core/com/jme3/app/Application.java +++ b/engine/src/core/com/jme3/app/Application.java 
@@ -290,6 +290,10 @@    private void initStateManager(){   stateManager = new AppStateManager(this);  +  + // Always register a ResetStatsState to make sure  + // that the stats are cleared every frame  + stateManager.attach(new ResetStatsState());   }     /**  @@ -580,6 +584,17 @@  }   task.invoke();   } while (((task = taskQueue.poll()) != null));  +  + /* I think the above is really just doing this:  + AppTask<?> task;  + while( (task = taskQueue.poll()) != null ) {  + if (!task.isCancelled()) {  + task.invoke();  + }  + }  + //...but it's hard to say for sure. It's so twisted  + //up that I don't trust my eyes. -pspeed  + */     if (speed == 0 || paused)   return;  
diff --git a/engine/src/core/com/jme3/app/ResetStatsState.java b/engine/src/core/com/jme3/app/ResetStatsState.java new file mode 100644 index 0000000..03f3013 --- /dev/null +++ b/engine/src/core/com/jme3/app/ResetStatsState.java 
@@ -0,0 +1,60 @@ +/* + * Copyright (c) 2009-2012 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.app; + +import com.jme3.app.state.AbstractAppState; +import com.jme3.renderer.RenderManager; + + +/** + * Resets (clearFrame()) the render's stats object every frame + * during AppState.render(). This state is registered once + * with Application to ensure that the stats are cleared once + * a frame. Using this makes sure that any Appliction based + * application that properly runs its state manager will have + * stats reset no matter how many views it has or if it even + * has views. + * + * @author Paul Speed + */ +public class ResetStatsState extends AbstractAppState { + + public ResetStatsState() { + }  + + @Override + public void render(RenderManager rm) { + super.render(rm); + rm.getRenderer().getStatistics().clearFrame();  + }  + +} 
diff --git a/engine/src/core/com/jme3/app/SimpleApplication.java b/engine/src/core/com/jme3/app/SimpleApplication.java index c79ce83..aa8e749 100644 --- a/engine/src/core/com/jme3/app/SimpleApplication.java +++ b/engine/src/core/com/jme3/app/SimpleApplication.java 
@@ -241,13 +241,6 @@  rootNode.updateGeometricState();  guiNode.updateGeometricState();   - // Moving this here to make sure it is always done. - // Now the sets are cleared every frame (guaranteed) - // and more than one viewer can access the data. This - // used to be cleared by StatsView but then only StatsView - // could get accurate counts. - renderer.getStatistics().clearFrame();  -   // render states  stateManager.render(renderManager);  renderManager.render(tpf, context.isRenderable()); 
diff --git a/engine/src/core/com/jme3/app/StatsView.java b/engine/src/core/com/jme3/app/StatsView.java index 49eeb13..f2b2ba5 100644 --- a/engine/src/core/com/jme3/app/StatsView.java +++ b/engine/src/core/com/jme3/app/StatsView.java 
@@ -105,7 +105,7 @@  labels[i].setText(stringBuilder);  }   - // Moved to SimpleApplication to make sure it is + // Moved to ResetStatsState to make sure it is  // done even if there is no StatsView or the StatsView  // is disable.  //statistics.clearFrame(); 
diff --git a/engine/src/core/com/jme3/app/state/AppStateManager.java b/engine/src/core/com/jme3/app/state/AppStateManager.java index 81228af..92a00f5 100644 --- a/engine/src/core/com/jme3/app/state/AppStateManager.java +++ b/engine/src/core/com/jme3/app/state/AppStateManager.java 
@@ -202,6 +202,9 @@    protected void initializePending(){   AppState[] array = getInitializing();  + if (array.length == 0)  + return;  +   synchronized( states ) {   // Move the states that will be initialized   // into the active array. In all but one case the  @@ -219,6 +222,9 @@    protected void terminatePending(){   AppState[] array = getTerminating();  + if (array.length == 0)  + return;  +   for (AppState state : array) {   state.cleanup();   }  
diff --git a/engine/src/core/com/jme3/input/InputManager.java b/engine/src/core/com/jme3/input/InputManager.java index 23f2988..a0c8788 100644 --- a/engine/src/core/com/jme3/input/InputManager.java +++ b/engine/src/core/com/jme3/input/InputManager.java 
@@ -1,881 +1,892 @@ -/* - * Copyright (c) 2009-2012 jMonkeyEngine - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of 'jMonkeyEngine' nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.jme3.input; - -import com.jme3.app.Application; -import com.jme3.input.controls.*; -import com.jme3.input.event.*; -import com.jme3.math.FastMath; -import com.jme3.math.Vector2f; -import com.jme3.util.IntMap; -import com.jme3.util.IntMap.Entry; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * The <code>InputManager</code> is responsible for converting input events - * received from the Key, Mouse and Joy Input implementations into an - * abstract, input device independent representation that user code can use. - * <p> - * By default an <code>InputManager</code> is included with every Application instance for use - * in user code to query input, unless the Application is created as headless - * or with input explicitly disabled. - * <p> - * The input manager has two concepts, a {@link Trigger} and a mapping. - * A trigger represents a specific input trigger, such as a key button,  - * or a mouse axis. A mapping represents a link onto one or several triggers,  - * when the appropriate trigger is activated (e.g. a key is pressed), the  - * mapping will be invoked. Any listeners registered to receive an event - * from the mapping will have an event raised. - * <p> - * There are two types of events that {@link InputListener input listeners} - * can receive, one is {@link ActionListener#onAction(java.lang.String, boolean, float) action} - * events and another is {@link AnalogListener#onAnalog(java.lang.String, float, float) analog} - * events.  - * <p> - * <code>onAction</code> events are raised when the specific input - * activates or deactivates. For a digital input such as key press, the <code>onAction()</code> - * event will be raised with the <code>isPressed</code> argument equal to true, - * when the key is released, <code>onAction</code> is called again but this time - * with the <code>isPressed</code> argument set to false. - * For analog inputs, the <code>onAction</code> method will be called any time - * the input is non-zero, however an exception to this is for joystick axis inputs, - * which are only called when the input is above the {@link InputManager#setAxisDeadZone(float) dead zone}. - * <p> - * <code>onAnalog</code> events are raised every frame while the input is activated. - * For digital inputs, every frame that the input is active will cause the - * <code>onAnalog</code> method to be called, the argument <code>value</code> - * argument will equal to the frame's time per frame (TPF) value but only - * for digital inputs. For analog inputs however, the <code>value</code> argument - * will equal the actual analog value. - */ -public class InputManager implements RawInputListener { - - private static final Logger logger = Logger.getLogger(InputManager.class.getName()); - private final KeyInput keys; - private final MouseInput mouse; - private final JoyInput joystick; - private final TouchInput touch; - private float frameTPF; - private long lastLastUpdateTime = 0; - private long lastUpdateTime = 0; - private long frameDelta = 0; - private long firstTime = 0; - private boolean eventsPermitted = false; - private boolean mouseVisible = true; - private boolean safeMode = false; - private float axisDeadZone = 0.05f; - private Vector2f cursorPos = new Vector2f(); - private Joystick[] joysticks; - private final IntMap<ArrayList<Mapping>> bindings = new IntMap<ArrayList<Mapping>>(); - private final HashMap<String, Mapping> mappings = new HashMap<String, Mapping>(); - private final IntMap<Long> pressedButtons = new IntMap<Long>(); - private final IntMap<Float> axisValues = new IntMap<Float>(); - private ArrayList<RawInputListener> rawListeners = new ArrayList<RawInputListener>(); - private RawInputListener[] rawListenerArray = null; - private ArrayList<InputEvent> inputQueue = new ArrayList<InputEvent>(); - - private static class Mapping { - - private final String name; - private final ArrayList<Integer> triggers = new ArrayList<Integer>(); - private final ArrayList<InputListener> listeners = new ArrayList<InputListener>(); - - public Mapping(String name) { - this.name = name; - } - } - - /** - * Initializes the InputManager. - *  - * <p>This should only be called internally in {@link Application}. - * - * @param mouse - * @param keys - * @param joystick - * @param touch - * @throws IllegalArgumentException If either mouseInput or keyInput are null. - */ - public InputManager(MouseInput mouse, KeyInput keys, JoyInput joystick, TouchInput touch) { - if (keys == null || mouse == null) { - throw new NullPointerException("Mouse or keyboard cannot be null"); - } - - this.keys = keys; - this.mouse = mouse; - this.joystick = joystick; - this.touch = touch; - - keys.setInputListener(this); - mouse.setInputListener(this); - if (joystick != null) { - joystick.setInputListener(this); - joysticks = joystick.loadJoysticks(this); - } - if (touch != null) { - touch.setInputListener(this); - } - - firstTime = keys.getInputTimeNanos(); - } - - private void invokeActions(int hash, boolean pressed) { - ArrayList<Mapping> maps = bindings.get(hash); - if (maps == null) { - return; - } - - int size = maps.size(); - for (int i = size - 1; i >= 0; i--) { - Mapping mapping = maps.get(i); - ArrayList<InputListener> listeners = mapping.listeners; - int listenerSize = listeners.size(); - for (int j = listenerSize - 1; j >= 0; j--) { - InputListener listener = listeners.get(j); - if (listener instanceof ActionListener) { - ((ActionListener) listener).onAction(mapping.name, pressed, frameTPF); - } - } - } - } - - private float computeAnalogValue(long timeDelta) { - if (safeMode || frameDelta == 0) { - return 1f; - } else { - return FastMath.clamp((float) timeDelta / (float) frameDelta, 0, 1); - } - } - - private void invokeTimedActions(int hash, long time, boolean pressed) { - if (!bindings.containsKey(hash)) { - return; - } - - if (pressed) { - pressedButtons.put(hash, time); - } else { - Long pressTimeObj = pressedButtons.remove(hash); - if (pressTimeObj == null) { - return; // under certain circumstances it can be null, ignore - } // the event then. - - long pressTime = pressTimeObj; - long lastUpdate = lastLastUpdateTime; - long releaseTime = time; - long timeDelta = releaseTime - Math.max(pressTime, lastUpdate); - - if (timeDelta > 0) { - invokeAnalogs(hash, computeAnalogValue(timeDelta), false); - } - } - } - - private void invokeUpdateActions() { - for (Entry<Long> pressedButton : pressedButtons) { - int hash = pressedButton.getKey(); - - long pressTime = pressedButton.getValue(); - long timeDelta = lastUpdateTime - Math.max(lastLastUpdateTime, pressTime); - - if (timeDelta > 0) { - invokeAnalogs(hash, computeAnalogValue(timeDelta), false); - } - } - - for (Entry<Float> axisValue : axisValues) { - int hash = axisValue.getKey(); - float value = axisValue.getValue(); - invokeAnalogs(hash, value * frameTPF, true); - } - } - - private void invokeAnalogs(int hash, float value, boolean isAxis) { - ArrayList<Mapping> maps = bindings.get(hash); - if (maps == null) { - return; - } - - if (!isAxis) { - value *= frameTPF; - } - - int size = maps.size(); - for (int i = size - 1; i >= 0; i--) { - Mapping mapping = maps.get(i); - ArrayList<InputListener> listeners = mapping.listeners; - int listenerSize = listeners.size(); - for (int j = listenerSize - 1; j >= 0; j--) { - InputListener listener = listeners.get(j); - if (listener instanceof AnalogListener) { - // NOTE: multiply by TPF for any button bindings - ((AnalogListener) listener).onAnalog(mapping.name, value, frameTPF); - } - } - } - } - - private void invokeAnalogsAndActions(int hash, float value, boolean applyTpf) { - if (value < axisDeadZone) { - invokeAnalogs(hash, value, !applyTpf); - return; - } - - ArrayList<Mapping> maps = bindings.get(hash); - if (maps == null) { - return; - } - - boolean valueChanged = !axisValues.containsKey(hash); - if (applyTpf) { - value *= frameTPF; - } - - int size = maps.size(); - for (int i = size - 1; i >= 0; i--) { - Mapping mapping = maps.get(i); - ArrayList<InputListener> listeners = mapping.listeners; - int listenerSize = listeners.size(); - for (int j = listenerSize - 1; j >= 0; j--) { - InputListener listener = listeners.get(j); - - if (listener instanceof ActionListener && valueChanged) { - ((ActionListener) listener).onAction(mapping.name, true, frameTPF); - } - - if (listener instanceof AnalogListener) { - ((AnalogListener) listener).onAnalog(mapping.name, value, frameTPF); - } - - } - } - } - - /** - * Callback from RawInputListener. Do not use. - */ - public void beginInput() { - } - - /** - * Callback from RawInputListener. Do not use. - */ - public void endInput() { - } - - private void onJoyAxisEventQueued(JoyAxisEvent evt) { -// for (int i = 0; i < rawListeners.size(); i++){ -// rawListeners.get(i).onJoyAxisEvent(evt); -// } - - int joyId = evt.getJoyIndex(); - int axis = evt.getAxisIndex(); - float value = evt.getValue(); - if (value < axisDeadZone && value > -axisDeadZone) { - int hash1 = JoyAxisTrigger.joyAxisHash(joyId, axis, true); - int hash2 = JoyAxisTrigger.joyAxisHash(joyId, axis, false); - - Float val1 = axisValues.get(hash1); - Float val2 = axisValues.get(hash2); - - if (val1 != null && val1.floatValue() > axisDeadZone) { - invokeActions(hash1, false); - } - if (val2 != null && val2.floatValue() > axisDeadZone) { - invokeActions(hash2, false); - } - - axisValues.remove(hash1); - axisValues.remove(hash2); - - } else if (value < 0) { - int hash = JoyAxisTrigger.joyAxisHash(joyId, axis, true); - int otherHash = JoyAxisTrigger.joyAxisHash(joyId, axis, false); - invokeAnalogsAndActions(hash, -value, true); - axisValues.put(hash, -value); - axisValues.remove(otherHash); - } else { - int hash = JoyAxisTrigger.joyAxisHash(joyId, axis, false); - int otherHash = JoyAxisTrigger.joyAxisHash(joyId, axis, true); - invokeAnalogsAndActions(hash, value, true); - axisValues.put(hash, value); - axisValues.remove(otherHash); - } - } - - /** - * Callback from RawInputListener. Do not use. - */ - public void onJoyAxisEvent(JoyAxisEvent evt) { - if (!eventsPermitted) { - throw new UnsupportedOperationException("JoyInput has raised an event at an illegal time."); - } - - inputQueue.add(evt); - } - - private void onJoyButtonEventQueued(JoyButtonEvent evt) { -// for (int i = 0; i < rawListeners.size(); i++){ -// rawListeners.get(i).onJoyButtonEvent(evt); -// } - - int hash = JoyButtonTrigger.joyButtonHash(evt.getJoyIndex(), evt.getButtonIndex()); - invokeActions(hash, evt.isPressed()); - invokeTimedActions(hash, evt.getTime(), evt.isPressed()); - } - - /** - * Callback from RawInputListener. Do not use. - */ - public void onJoyButtonEvent(JoyButtonEvent evt) { - if (!eventsPermitted) { - throw new UnsupportedOperationException("JoyInput has raised an event at an illegal time."); - } - - inputQueue.add(evt); - } - - private void onMouseMotionEventQueued(MouseMotionEvent evt) { -// for (int i = 0; i < rawListeners.size(); i++){ -// rawListeners.get(i).onMouseMotionEvent(evt); -// } - - if (evt.getDX() != 0) { - float val = Math.abs(evt.getDX()) / 1024f; - invokeAnalogsAndActions(MouseAxisTrigger.mouseAxisHash(MouseInput.AXIS_X, evt.getDX() < 0), val, false); - } - if (evt.getDY() != 0) { - float val = Math.abs(evt.getDY()) / 1024f; - invokeAnalogsAndActions(MouseAxisTrigger.mouseAxisHash(MouseInput.AXIS_Y, evt.getDY() < 0), val, false); - } - if (evt.getDeltaWheel() != 0) { - float val = Math.abs(evt.getDeltaWheel()) / 100f; - invokeAnalogsAndActions(MouseAxisTrigger.mouseAxisHash(MouseInput.AXIS_WHEEL, evt.getDeltaWheel() < 0), val, false); - } - } - - /** - * Callback from RawInputListener. Do not use. - */ - public void onMouseMotionEvent(MouseMotionEvent evt) { - if (!eventsPermitted) { - throw new UnsupportedOperationException("MouseInput has raised an event at an illegal time."); - } - - cursorPos.set(evt.getX(), evt.getY()); - inputQueue.add(evt); - } - - private void onMouseButtonEventQueued(MouseButtonEvent evt) { - int hash = MouseButtonTrigger.mouseButtonHash(evt.getButtonIndex()); - invokeActions(hash, evt.isPressed()); - invokeTimedActions(hash, evt.getTime(), evt.isPressed()); - } - - /** - * Callback from RawInputListener. Do not use. - */ - public void onMouseButtonEvent(MouseButtonEvent evt) { - if (!eventsPermitted) { - throw new UnsupportedOperationException("MouseInput has raised an event at an illegal time."); - } - //updating cursor pos on click, so that non android touch events can properly update cursor position. - cursorPos.set(evt.getX(), evt.getY()); - inputQueue.add(evt); - } - - private void onKeyEventQueued(KeyInputEvent evt) { - if (evt.isRepeating()) { - return; // repeat events not used for bindings - } -  - int hash = KeyTrigger.keyHash(evt.getKeyCode()); - invokeActions(hash, evt.isPressed()); - invokeTimedActions(hash, evt.getTime(), evt.isPressed()); - } - - /** - * Callback from RawInputListener. Do not use. - */ - public void onKeyEvent(KeyInputEvent evt) { - if (!eventsPermitted) { - throw new UnsupportedOperationException("KeyInput has raised an event at an illegal time."); - } - - inputQueue.add(evt); - } - - /** - * Set the deadzone for joystick axes. - *  - * <p>{@link ActionListener#onAction(java.lang.String, boolean, float) } - * events will only be raised if the joystick axis value is greater than - * the <code>deadZone</code>. - *  - * @param deadZone the deadzone for joystick axes.  - */ - public void setAxisDeadZone(float deadZone) { - this.axisDeadZone = deadZone; - } - - /** - * Returns the deadzone for joystick axes. - *  - * @return the deadzone for joystick axes. - */ - public float getAxisDeadZone() { - return axisDeadZone; - } -  - /** - * Adds a new listener to receive events on the given mappings. - *  - * <p>The given InputListener will be registered to receive events - * on the specified mapping names. When a mapping raises an event, the - * listener will have its appropriate method invoked, either - * {@link ActionListener#onAction(java.lang.String, boolean, float) } - * or {@link AnalogListener#onAnalog(java.lang.String, float, float) } - * depending on which interface the <code>listener</code> implements.  - * If the listener implements both interfaces, then it will receive the - * appropriate event for each method. - *  - * @param listener The listener to register to receive input events. - * @param mappingNames The mapping names which the listener will receive - * events from. - *  - * @see InputManager#removeListener(com.jme3.input.controls.InputListener)  - */ - public void addListener(InputListener listener, String... mappingNames) { - for (String mappingName : mappingNames) { - Mapping mapping = mappings.get(mappingName); - if (mapping == null) { - mapping = new Mapping(mappingName); - mappings.put(mappingName, mapping); - } - if (!mapping.listeners.contains(listener)) { - mapping.listeners.add(listener); - } - } - } - - /** - * Removes a listener from receiving events. - *  - * <p>This will unregister the listener from any mappings that it - * was previously registered with via  - * {@link InputManager#addListener(com.jme3.input.controls.InputListener, java.lang.String[]) }. - *  - * @param listener The listener to unregister. - *  - * @see InputManager#addListener(com.jme3.input.controls.InputListener, java.lang.String[])  - */ - public void removeListener(InputListener listener) { - for (Mapping mapping : mappings.values()) { - mapping.listeners.remove(listener); - } - } - - /** - * Create a new mapping to the given triggers. - *  - * <p> - * The given mapping will be assigned to the given triggers, when - * any of the triggers given raise an event, the listeners - * registered to the mappings will receive appropriate events. - *  - * @param mappingName The mapping name to assign. - * @param triggers The triggers to which the mapping is to be registered. - *  - * @see InputManager#deleteMapping(java.lang.String)  - */ - public void addMapping(String mappingName, Trigger... triggers) { - Mapping mapping = mappings.get(mappingName); - if (mapping == null) { - mapping = new Mapping(mappingName); - mappings.put(mappingName, mapping); - } - - for (Trigger trigger : triggers) { - int hash = trigger.triggerHashCode(); - ArrayList<Mapping> names = bindings.get(hash); - if (names == null) { - names = new ArrayList<Mapping>(); - bindings.put(hash, names); - } - if (!names.contains(mapping)) { - names.add(mapping); - mapping.triggers.add(hash); - } else { - logger.log(Level.WARNING, "Attempted to add mapping \"{0}\" twice to trigger.", mappingName); - } - } - } - - /** - * Returns true if this InputManager has a mapping registered - * for the given mappingName. - * - * @param mappingName The mapping name to check. - * - * @see InputManager#addMapping(java.lang.String, com.jme3.input.controls.Trigger[])  - * @see InputManager#deleteMapping(java.lang.String)  - */  - public boolean hasMapping(String mappingName) { - return mappings.containsKey(mappingName); - } -  - /** - * Deletes a mapping from receiving trigger events. - *  - * <p> - * The given mapping will no longer be assigned to receive trigger - * events. - *  - * @param mappingName The mapping name to unregister. - *  - * @see InputManager#addMapping(java.lang.String, com.jme3.input.controls.Trigger[])  - */ - public void deleteMapping(String mappingName) { - Mapping mapping = mappings.remove(mappingName); - if (mapping == null) { - throw new IllegalArgumentException("Cannot find mapping: " + mappingName); - } - - ArrayList<Integer> triggers = mapping.triggers; - for (int i = triggers.size() - 1; i >= 0; i--) { - int hash = triggers.get(i); - ArrayList<Mapping> maps = bindings.get(hash); - maps.remove(mapping); - } - } - - /** - * Deletes a specific trigger registered to a mapping. - *  - * <p> - * The given mapping will no longer receive events raised by the  - * trigger. - *  - * @param mappingName The mapping name to cease receiving events from the  - * trigger. - * @param trigger The trigger to no longer invoke events on the mapping. - */ - public void deleteTrigger(String mappingName, Trigger trigger) { - Mapping mapping = mappings.get(mappingName); - if (mapping == null) { - throw new IllegalArgumentException("Cannot find mapping: " + mappingName); - } - - ArrayList<Mapping> maps = bindings.get(trigger.triggerHashCode()); - maps.remove(mapping); - - } - - /** - * Clears all the input mappings from this InputManager.  - * Consequently, also clears all of the - * InputListeners as well. - */ - public void clearMappings() { - mappings.clear(); - bindings.clear(); - reset(); - } - - /** - * Do not use. - * Called to reset pressed keys or buttons when focus is restored. - */ - public void reset() { - pressedButtons.clear(); - axisValues.clear(); - } - - /** - * Returns whether the mouse cursor is visible or not. - *  - * <p>By default the cursor is visible. - *  - * @return whether the mouse cursor is visible or not. - *  - * @see InputManager#setCursorVisible(boolean)  - */ - public boolean isCursorVisible() { - return mouseVisible; - } - - /** - * Set whether the mouse cursor should be visible or not. - *  - * @param visible whether the mouse cursor should be visible or not. - */ - public void setCursorVisible(boolean visible) { - if (mouseVisible != visible) { - mouseVisible = visible; - mouse.setCursorVisible(mouseVisible); - } - } - - /** - * Returns the current cursor position. The position is relative to the - * bottom-left of the screen and is in pixels. - *  - * @return the current cursor position - */ - public Vector2f getCursorPosition() { - return cursorPos; - } - - /** - * Returns an array of all joysticks installed on the system. - *  - * @return an array of all joysticks installed on the system. - */ - public Joystick[] getJoysticks() { - return joysticks; - } - - /** - * Adds a {@link RawInputListener} to receive raw input events. - *  - * <p> - * Any raw input listeners registered to this <code>InputManager</code> - * will receive raw input events first, before they get handled - * by the <code>InputManager</code> itself. The listeners are  - * each processed in the order they were added, e.g. FIFO. - * <p> - * If a raw input listener has handled the event and does not wish - * other listeners down the list to process the event, it may set the - * {@link InputEvent#setConsumed() consumed flag} to indicate the  - * event was consumed and shouldn't be processed any further. - * The listener may do this either at each of the event callbacks  - * or at the {@link RawInputListener#endInput() } method. - *  - * @param listener A listener to receive raw input events. - *  - * @see RawInputListener - */ - public void addRawInputListener(RawInputListener listener) { - rawListeners.add(listener); - rawListenerArray = null; - } - - /** - * Removes a {@link RawInputListener} so that it no longer - * receives raw input events. - *  - * @param listener The listener to cease receiving raw input events. - *  - * @see InputManager#addRawInputListener(com.jme3.input.RawInputListener)  - */ - public void removeRawInputListener(RawInputListener listener) { - rawListeners.remove(listener); - rawListenerArray = null; - } - - /** - * Clears all {@link RawInputListener}s. - *  - * @see InputManager#addRawInputListener(com.jme3.input.RawInputListener)  - */ - public void clearRawInputListeners() { - rawListeners.clear(); - rawListenerArray = null; - } - - private RawInputListener[] getRawListenerArray() { - if (rawListenerArray == null)  - rawListenerArray = rawListeners.toArray(new RawInputListener[rawListeners.size()]); - return rawListenerArray; - } -  - /** - * Enable simulation of mouse events. Used for touchscreen input only. - *  - * @param value True to enable simulation of mouse events - */ - public void setSimulateMouse(boolean value) { - if (touch != null) { - touch.setSimulateMouse(value); - } - } -  - /** - * Enable simulation of keyboard events. Used for touchscreen input only. - *  - * @param value True to enable simulation of keyboard events - */ - public void setSimulateKeyboard(boolean value) { - if (touch != null) { - touch.setSimulateKeyboard(value); - } - } - - private void processQueue() { - int queueSize = inputQueue.size(); - RawInputListener[] array = getRawListenerArray(); -  - for (RawInputListener listener : array) { - listener.beginInput(); - - for (int j = 0; j < queueSize; j++) { - InputEvent event = inputQueue.get(j); - if (event.isConsumed()) { - continue; - } - - if (event instanceof MouseMotionEvent) { - listener.onMouseMotionEvent((MouseMotionEvent) event); - } else if (event instanceof KeyInputEvent) { - listener.onKeyEvent((KeyInputEvent) event); - } else if (event instanceof MouseButtonEvent) { - listener.onMouseButtonEvent((MouseButtonEvent) event); - } else if (event instanceof JoyAxisEvent) { - listener.onJoyAxisEvent((JoyAxisEvent) event); - } else if (event instanceof JoyButtonEvent) { - listener.onJoyButtonEvent((JoyButtonEvent) event); - } else if (event instanceof TouchEvent) { - listener.onTouchEvent((TouchEvent) event); - } else { - assert false; - } - } - - listener.endInput(); - } - - for (int i = 0; i < queueSize; i++) { - InputEvent event = inputQueue.get(i); - if (event.isConsumed()) { - continue; - } - - if (event instanceof MouseMotionEvent) { - onMouseMotionEventQueued((MouseMotionEvent) event); - } else if (event instanceof KeyInputEvent) { - onKeyEventQueued((KeyInputEvent) event); - } else if (event instanceof MouseButtonEvent) { - onMouseButtonEventQueued((MouseButtonEvent) event); - } else if (event instanceof JoyAxisEvent) { - onJoyAxisEventQueued((JoyAxisEvent) event); - } else if (event instanceof JoyButtonEvent) { - onJoyButtonEventQueued((JoyButtonEvent) event); - } else if (event instanceof TouchEvent) { - onTouchEventQueued((TouchEvent) event); - } else { - assert false; - } - // larynx, 2011.06.10 - flag event as reusable because - // the android input uses a non-allocating ringbuffer which - // needs to know when the event is not anymore in inputQueue - // and therefor can be reused. - event.setConsumed(); - } - - inputQueue.clear(); - } - - /** - * Updates the <code>InputManager</code>.  - * This will query current input devices and send - * appropriate events to registered listeners. - * - * @param tpf Time per frame value. - */ - public void update(float tpf) { - frameTPF = tpf; -  - // Activate safemode if the TPF value is so small - // that rounding errors are inevitable - safeMode = tpf < 0.015f; -  - long currentTime = keys.getInputTimeNanos(); - frameDelta = currentTime - lastUpdateTime; - - eventsPermitted = true; - - keys.update(); - mouse.update(); - if (joystick != null) { - joystick.update(); - } - if (touch != null) { - touch.update(); - } - - eventsPermitted = false; - - processQueue(); - invokeUpdateActions(); - - lastLastUpdateTime = lastUpdateTime; - lastUpdateTime = currentTime; - } - - /** - * Dispatches touch events to touch listeners - * @param evt The touch event to be dispatched to all onTouch listeners - */ - public void onTouchEventQueued(TouchEvent evt) {  - ArrayList<Mapping> maps = bindings.get(TouchTrigger.touchHash(evt.getKeyCode())); - if (maps == null) { - return; - } - - int size = maps.size(); - for (int i = size - 1; i >= 0; i--) { - Mapping mapping = maps.get(i); - ArrayList<InputListener> listeners = mapping.listeners; - int listenerSize = listeners.size(); - for (int j = listenerSize - 1; j >= 0; j--) { - InputListener listener = listeners.get(j); - if (listener instanceof TouchListener) { - ((TouchListener) listener).onTouch(mapping.name, evt, frameTPF);  - } - } - }  - } -  - /** - * Callback from RawInputListener. Do not use. - */ - @Override - public void onTouchEvent(TouchEvent evt) { - if (!eventsPermitted) { - throw new UnsupportedOperationException("TouchInput has raised an event at an illegal time."); - } - inputQueue.add(evt);  - } -} +/*  + * Copyright (c) 2009-2012 jMonkeyEngine  + * All rights reserved.  + *  + * Redistribution and use in source and binary forms, with or without  + * modification, are permitted provided that the following conditions are  + * met:  + *  + * * Redistributions of source code must retain the above copyright  + * notice, this list of conditions and the following disclaimer.  + *  + * * Redistributions in binary form must reproduce the above copyright  + * notice, this list of conditions and the following disclaimer in the  + * documentation and/or other materials provided with the distribution.  + *  + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors  + * may be used to endorse or promote products derived from this software  + * without specific prior written permission.  + *  + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED  + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR  + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR  + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,  + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,  + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR  + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF  + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING  + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS  + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  + */  +package com.jme3.input;  +  +import com.jme3.app.Application;  +import com.jme3.input.controls.*;  +import com.jme3.input.event.*;  +import com.jme3.math.FastMath;  +import com.jme3.math.Vector2f;  +import com.jme3.util.IntMap;  +import com.jme3.util.IntMap.Entry;  +import java.util.ArrayList;  +import java.util.HashMap;  +import java.util.logging.Level;  +import java.util.logging.Logger;  +  +/**  + * The <code>InputManager</code> is responsible for converting input events  + * received from the Key, Mouse and Joy Input implementations into an  + * abstract, input device independent representation that user code can use.  + * <p>  + * By default an <code>InputManager</code> is included with every Application instance for use  + * in user code to query input, unless the Application is created as headless  + * or with input explicitly disabled.  + * <p>  + * The input manager has two concepts, a {@link Trigger} and a mapping.  + * A trigger represents a specific input trigger, such as a key button,  + * or a mouse axis. A mapping represents a link onto one or several triggers,  + * when the appropriate trigger is activated (e.g. a key is pressed), the  + * mapping will be invoked. Any listeners registered to receive an event  + * from the mapping will have an event raised.  + * <p>  + * There are two types of events that {@link InputListener input listeners}  + * can receive, one is {@link ActionListener#onAction(java.lang.String, boolean, float) action}  + * events and another is {@link AnalogListener#onAnalog(java.lang.String, float, float) analog}  + * events.  + * <p>  + * <code>onAction</code> events are raised when the specific input  + * activates or deactivates. For a digital input such as key press, the <code>onAction()</code>  + * event will be raised with the <code>isPressed</code> argument equal to true,  + * when the key is released, <code>onAction</code> is called again but this time  + * with the <code>isPressed</code> argument set to false.  + * For analog inputs, the <code>onAction</code> method will be called any time  + * the input is non-zero, however an exception to this is for joystick axis inputs,  + * which are only called when the input is above the {@link InputManager#setAxisDeadZone(float) dead zone}.  + * <p>  + * <code>onAnalog</code> events are raised every frame while the input is activated.  + * For digital inputs, every frame that the input is active will cause the  + * <code>onAnalog</code> method to be called, the argument <code>value</code>  + * argument will equal to the frame's time per frame (TPF) value but only  + * for digital inputs. For analog inputs however, the <code>value</code> argument  + * will equal the actual analog value.  + */  +public class InputManager implements RawInputListener {  +  + private static final Logger logger = Logger.getLogger(InputManager.class.getName());  + private final KeyInput keys;  + private final MouseInput mouse;  + private final JoyInput joystick;  + private final TouchInput touch;  + private float frameTPF;  + private long lastLastUpdateTime = 0;  + private long lastUpdateTime = 0;  + private long frameDelta = 0;  + private long firstTime = 0;  + private boolean eventsPermitted = false;  + private boolean mouseVisible = true;  + private boolean safeMode = false;  + private float axisDeadZone = 0.05f;  + private Vector2f cursorPos = new Vector2f();  + private Joystick[] joysticks;  + private final IntMap<ArrayList<Mapping>> bindings = new IntMap<ArrayList<Mapping>>();  + private final HashMap<String, Mapping> mappings = new HashMap<String, Mapping>();  + private final IntMap<Long> pressedButtons = new IntMap<Long>();  + private final IntMap<Float> axisValues = new IntMap<Float>();  + private ArrayList<RawInputListener> rawListeners = new ArrayList<RawInputListener>();  + private RawInputListener[] rawListenerArray = null;  + private ArrayList<InputEvent> inputQueue = new ArrayList<InputEvent>();  +  + private static class Mapping {  +  + private final String name;  + private final ArrayList<Integer> triggers = new ArrayList<Integer>();  + private final ArrayList<InputListener> listeners = new ArrayList<InputListener>();  +  + public Mapping(String name) {  + this.name = name;  + }  + }  +  + /**  + * Initializes the InputManager.  + *  + * <p>This should only be called internally in {@link Application}.  + *  + * @param mouse  + * @param keys  + * @param joystick  + * @param touch  + * @throws IllegalArgumentException If either mouseInput or keyInput are null.  + */  + public InputManager(MouseInput mouse, KeyInput keys, JoyInput joystick, TouchInput touch) {  + if (keys == null || mouse == null) {  + throw new NullPointerException("Mouse or keyboard cannot be null");  + }  +  + this.keys = keys;  + this.mouse = mouse;  + this.joystick = joystick;  + this.touch = touch;  +  + keys.setInputListener(this);  + mouse.setInputListener(this);  + if (joystick != null) {  + joystick.setInputListener(this);  + joysticks = joystick.loadJoysticks(this);  + }  + if (touch != null) {  + touch.setInputListener(this);  + }  +  + firstTime = keys.getInputTimeNanos();  + }  +  + private void invokeActions(int hash, boolean pressed) {  + ArrayList<Mapping> maps = bindings.get(hash);  + if (maps == null) {  + return;  + }  +  + int size = maps.size();  + for (int i = size - 1; i >= 0; i--) {  + Mapping mapping = maps.get(i);  + ArrayList<InputListener> listeners = mapping.listeners;  + int listenerSize = listeners.size();  + for (int j = listenerSize - 1; j >= 0; j--) {  + InputListener listener = listeners.get(j);  + if (listener instanceof ActionListener) {  + ((ActionListener) listener).onAction(mapping.name, pressed, frameTPF);  + }  + }  + }  + }  +  + private float computeAnalogValue(long timeDelta) {  + if (safeMode || frameDelta == 0) {  + return 1f;  + } else {  + return FastMath.clamp((float) timeDelta / (float) frameDelta, 0, 1);  + }  + }  +  + private void invokeTimedActions(int hash, long time, boolean pressed) {  + if (!bindings.containsKey(hash)) {  + return;  + }  +  + if (pressed) {  + pressedButtons.put(hash, time);  + } else {  + Long pressTimeObj = pressedButtons.remove(hash);  + if (pressTimeObj == null) {  + return; // under certain circumstances it can be null, ignore  + } // the event then.  +  + long pressTime = pressTimeObj;  + long lastUpdate = lastLastUpdateTime;  + long releaseTime = time;  + long timeDelta = releaseTime - Math.max(pressTime, lastUpdate);  +  + if (timeDelta > 0) {  + invokeAnalogs(hash, computeAnalogValue(timeDelta), false);  + }  + }  + }  +  + private void invokeUpdateActions() {  + if (pressedButtons.size() > 0) for (Entry<Long> pressedButton : pressedButtons) {  + int hash = pressedButton.getKey();  +  + long pressTime = pressedButton.getValue();  + long timeDelta = lastUpdateTime - Math.max(lastLastUpdateTime, pressTime);  +  + if (timeDelta > 0) {  + invokeAnalogs(hash, computeAnalogValue(timeDelta), false);  + }  + }  +  + if (axisValues.size() > 0) for (Entry<Float> axisValue : axisValues) {  + int hash = axisValue.getKey();  + float value = axisValue.getValue();  + invokeAnalogs(hash, value * frameTPF, true);  + }  + }  +  + private void invokeAnalogs(int hash, float value, boolean isAxis) {  + ArrayList<Mapping> maps = bindings.get(hash);  + if (maps == null) {  + return;  + }  +  + if (!isAxis) {  + value *= frameTPF;  + }  +  + int size = maps.size();  + for (int i = size - 1; i >= 0; i--) {  + Mapping mapping = maps.get(i);  + ArrayList<InputListener> listeners = mapping.listeners;  + int listenerSize = listeners.size();  + for (int j = listenerSize - 1; j >= 0; j--) {  + InputListener listener = listeners.get(j);  + if (listener instanceof AnalogListener) {  + // NOTE: multiply by TPF for any button bindings  + ((AnalogListener) listener).onAnalog(mapping.name, value, frameTPF);  + }  + }  + }  + }  +  + private void invokeAnalogsAndActions(int hash, float value, boolean applyTpf) {  + if (value < axisDeadZone) {  + invokeAnalogs(hash, value, !applyTpf);  + return;  + }  +  + ArrayList<Mapping> maps = bindings.get(hash);  + if (maps == null) {  + return;  + }  +  + boolean valueChanged = !axisValues.containsKey(hash);  + if (applyTpf) {  + value *= frameTPF;  + }  +  + int size = maps.size();  + for (int i = size - 1; i >= 0; i--) {  + Mapping mapping = maps.get(i);  + ArrayList<InputListener> listeners = mapping.listeners;  + int listenerSize = listeners.size();  + for (int j = listenerSize - 1; j >= 0; j--) {  + InputListener listener = listeners.get(j);  +  + if (listener instanceof ActionListener && valueChanged) {  + ((ActionListener) listener).onAction(mapping.name, true, frameTPF);  + }  +  + if (listener instanceof AnalogListener) {  + ((AnalogListener) listener).onAnalog(mapping.name, value, frameTPF);  + }  +  + }  + }  + }  +  + /**  + * Callback from RawInputListener. Do not use.  + */  + public void beginInput() {  + }  +  + /**  + * Callback from RawInputListener. Do not use.  + */  + public void endInput() {  + }  +  + private void onJoyAxisEventQueued(JoyAxisEvent evt) {  +// for (int i = 0; i < rawListeners.size(); i++){  +// rawListeners.get(i).onJoyAxisEvent(evt);  +// }  +  + int joyId = evt.getJoyIndex();  + int axis = evt.getAxisIndex();  + float value = evt.getValue();  + if (value < axisDeadZone && value > -axisDeadZone) {  + int hash1 = JoyAxisTrigger.joyAxisHash(joyId, axis, true);  + int hash2 = JoyAxisTrigger.joyAxisHash(joyId, axis, false);  +  + Float val1 = axisValues.get(hash1);  + Float val2 = axisValues.get(hash2);  +  + if (val1 != null && val1.floatValue() > axisDeadZone) {  + invokeActions(hash1, false);  + }  + if (val2 != null && val2.floatValue() > axisDeadZone) {  + invokeActions(hash2, false);  + }  +  + axisValues.remove(hash1);  + axisValues.remove(hash2);  +  + } else if (value < 0) {  + int hash = JoyAxisTrigger.joyAxisHash(joyId, axis, true);  + int otherHash = JoyAxisTrigger.joyAxisHash(joyId, axis, false);  + invokeAnalogsAndActions(hash, -value, true);  + axisValues.put(hash, -value);  + axisValues.remove(otherHash);  + } else {  + int hash = JoyAxisTrigger.joyAxisHash(joyId, axis, false);  + int otherHash = JoyAxisTrigger.joyAxisHash(joyId, axis, true);  + invokeAnalogsAndActions(hash, value, true);  + axisValues.put(hash, value);  + axisValues.remove(otherHash);  + }  + }  +  + /**  + * Callback from RawInputListener. Do not use.  + */  + public void onJoyAxisEvent(JoyAxisEvent evt) {  + if (!eventsPermitted) {  + throw new UnsupportedOperationException("JoyInput has raised an event at an illegal time.");  + }  +  + inputQueue.add(evt);  + }  +  + private void onJoyButtonEventQueued(JoyButtonEvent evt) {  +// for (int i = 0; i < rawListeners.size(); i++){  +// rawListeners.get(i).onJoyButtonEvent(evt);  +// }  +  + int hash = JoyButtonTrigger.joyButtonHash(evt.getJoyIndex(), evt.getButtonIndex());  + invokeActions(hash, evt.isPressed());  + invokeTimedActions(hash, evt.getTime(), evt.isPressed());  + }  +  + /**  + * Callback from RawInputListener. Do not use.  + */  + public void onJoyButtonEvent(JoyButtonEvent evt) {  + if (!eventsPermitted) {  + throw new UnsupportedOperationException("JoyInput has raised an event at an illegal time.");  + }  +  + inputQueue.add(evt);  + }  +  + private void onMouseMotionEventQueued(MouseMotionEvent evt) {  +// for (int i = 0; i < rawListeners.size(); i++){  +// rawListeners.get(i).onMouseMotionEvent(evt);  +// }  +  + if (evt.getDX() != 0) {  + float val = Math.abs(evt.getDX()) / 1024f;  + invokeAnalogsAndActions(MouseAxisTrigger.mouseAxisHash(MouseInput.AXIS_X, evt.getDX() < 0), val, false);  + }  + if (evt.getDY() != 0) {  + float val = Math.abs(evt.getDY()) / 1024f;  + invokeAnalogsAndActions(MouseAxisTrigger.mouseAxisHash(MouseInput.AXIS_Y, evt.getDY() < 0), val, false);  + }  + if (evt.getDeltaWheel() != 0) {  + float val = Math.abs(evt.getDeltaWheel()) / 100f;  + invokeAnalogsAndActions(MouseAxisTrigger.mouseAxisHash(MouseInput.AXIS_WHEEL, evt.getDeltaWheel() < 0), val, false);  + }  + }  +  + /**  + * Callback from RawInputListener. Do not use.  + */  + public void onMouseMotionEvent(MouseMotionEvent evt) {  + if (!eventsPermitted) {  + throw new UnsupportedOperationException("MouseInput has raised an event at an illegal time.");  + }  +  + cursorPos.set(evt.getX(), evt.getY());  + inputQueue.add(evt);  + }  +  + private void onMouseButtonEventQueued(MouseButtonEvent evt) {  + int hash = MouseButtonTrigger.mouseButtonHash(evt.getButtonIndex());  + invokeActions(hash, evt.isPressed());  + invokeTimedActions(hash, evt.getTime(), evt.isPressed());  + }  +  + /**  + * Callback from RawInputListener. Do not use.  + */  + public void onMouseButtonEvent(MouseButtonEvent evt) {  + if (!eventsPermitted) {  + throw new UnsupportedOperationException("MouseInput has raised an event at an illegal time.");  + }  + //updating cursor pos on click, so that non android touch events can properly update cursor position.  + cursorPos.set(evt.getX(), evt.getY());  + inputQueue.add(evt);  + }  +  + private void onKeyEventQueued(KeyInputEvent evt) {  + if (evt.isRepeating()) {  + return; // repeat events not used for bindings  + }  +  + int hash = KeyTrigger.keyHash(evt.getKeyCode());  + invokeActions(hash, evt.isPressed());  + invokeTimedActions(hash, evt.getTime(), evt.isPressed());  + }  +  + /**  + * Callback from RawInputListener. Do not use.  + */  + public void onKeyEvent(KeyInputEvent evt) {  + if (!eventsPermitted) {  + throw new UnsupportedOperationException("KeyInput has raised an event at an illegal time.");  + }  +  + inputQueue.add(evt);  + }  +  + /**  + * Set the deadzone for joystick axes.  + *  + * <p>{@link ActionListener#onAction(java.lang.String, boolean, float) }  + * events will only be raised if the joystick axis value is greater than  + * the <code>deadZone</code>.  + *  + * @param deadZone the deadzone for joystick axes.  + */  + public void setAxisDeadZone(float deadZone) {  + this.axisDeadZone = deadZone;  + }  +  + /**  + * Returns the deadzone for joystick axes.  + *  + * @return the deadzone for joystick axes.  + */  + public float getAxisDeadZone() {  + return axisDeadZone;  + }  +  + /**  + * Adds a new listener to receive events on the given mappings.  + *  + * <p>The given InputListener will be registered to receive events  + * on the specified mapping names. When a mapping raises an event, the  + * listener will have its appropriate method invoked, either  + * {@link ActionListener#onAction(java.lang.String, boolean, float) }  + * or {@link AnalogListener#onAnalog(java.lang.String, float, float) }  + * depending on which interface the <code>listener</code> implements.  + * If the listener implements both interfaces, then it will receive the  + * appropriate event for each method.  + *  + * @param listener The listener to register to receive input events.  + * @param mappingNames The mapping names which the listener will receive  + * events from.  + *  + * @see InputManager#removeListener(com.jme3.input.controls.InputListener)  + */  + public void addListener(InputListener listener, String... mappingNames) {  + for (String mappingName : mappingNames) {  + Mapping mapping = mappings.get(mappingName);  + if (mapping == null) {  + mapping = new Mapping(mappingName);  + mappings.put(mappingName, mapping);  + }  + if (!mapping.listeners.contains(listener)) {  + mapping.listeners.add(listener);  + }  + }  + }  +  + /**  + * Removes a listener from receiving events.  + *  + * <p>This will unregister the listener from any mappings that it  + * was previously registered with via  + * {@link InputManager#addListener(com.jme3.input.controls.InputListener, java.lang.String[]) }.  + *  + * @param listener The listener to unregister.  + *  + * @see InputManager#addListener(com.jme3.input.controls.InputListener, java.lang.String[])  + */  + public void removeListener(InputListener listener) {  + for (Mapping mapping : mappings.values()) {  + mapping.listeners.remove(listener);  + }  + }  +  + /**  + * Create a new mapping to the given triggers.  + *  + * <p>  + * The given mapping will be assigned to the given triggers, when  + * any of the triggers given raise an event, the listeners  + * registered to the mappings will receive appropriate events.  + *  + * @param mappingName The mapping name to assign.  + * @param triggers The triggers to which the mapping is to be registered.  + *  + * @see InputManager#deleteMapping(java.lang.String)  + */  + public void addMapping(String mappingName, Trigger... triggers) {  + Mapping mapping = mappings.get(mappingName);  + if (mapping == null) {  + mapping = new Mapping(mappingName);  + mappings.put(mappingName, mapping);  + }  +  + for (Trigger trigger : triggers) {  + int hash = trigger.triggerHashCode();  + ArrayList<Mapping> names = bindings.get(hash);  + if (names == null) {  + names = new ArrayList<Mapping>();  + bindings.put(hash, names);  + }  + if (!names.contains(mapping)) {  + names.add(mapping);  + mapping.triggers.add(hash);  + } else {  + logger.log(Level.WARNING, "Attempted to add mapping \"{0}\" twice to trigger.", mappingName);  + }  + }  + }  +  + /**  + * Returns true if this InputManager has a mapping registered  + * for the given mappingName.  + *  + * @param mappingName The mapping name to check.  + *  + * @see InputManager#addMapping(java.lang.String, com.jme3.input.controls.Trigger[])  + * @see InputManager#deleteMapping(java.lang.String)  + */  + public boolean hasMapping(String mappingName) {  + return mappings.containsKey(mappingName);  + }  +  + /**  + * Deletes a mapping from receiving trigger events.  + *  + * <p>  + * The given mapping will no longer be assigned to receive trigger  + * events.  + *  + * @param mappingName The mapping name to unregister.  + *  + * @see InputManager#addMapping(java.lang.String, com.jme3.input.controls.Trigger[])  + */  + public void deleteMapping(String mappingName) {  + Mapping mapping = mappings.remove(mappingName);  + if (mapping == null) {  + throw new IllegalArgumentException("Cannot find mapping: " + mappingName);  + }  +  + ArrayList<Integer> triggers = mapping.triggers;  + for (int i = triggers.size() - 1; i >= 0; i--) {  + int hash = triggers.get(i);  + ArrayList<Mapping> maps = bindings.get(hash);  + maps.remove(mapping);  + }  + }  +  + /**  + * Deletes a specific trigger registered to a mapping.  + *  + * <p>  + * The given mapping will no longer receive events raised by the  + * trigger.  + *  + * @param mappingName The mapping name to cease receiving events from the  + * trigger.  + * @param trigger The trigger to no longer invoke events on the mapping.  + */  + public void deleteTrigger(String mappingName, Trigger trigger) {  + Mapping mapping = mappings.get(mappingName);  + if (mapping == null) {  + throw new IllegalArgumentException("Cannot find mapping: " + mappingName);  + }  +  + ArrayList<Mapping> maps = bindings.get(trigger.triggerHashCode());  + maps.remove(mapping);  +  + }  +  + /**  + * Clears all the input mappings from this InputManager.  + * Consequently, also clears all of the  + * InputListeners as well.  + */  + public void clearMappings() {  + mappings.clear();  + bindings.clear();  + reset();  + }  +  + /**  + * Do not use.  + * Called to reset pressed keys or buttons when focus is restored.  + */  + public void reset() {  + pressedButtons.clear();  + axisValues.clear();  + }  +  + /**  + * Returns whether the mouse cursor is visible or not.  + *  + * <p>By default the cursor is visible.  + *  + * @return whether the mouse cursor is visible or not.  + *  + * @see InputManager#setCursorVisible(boolean)  + */  + public boolean isCursorVisible() {  + return mouseVisible;  + }  +  + /**  + * Set whether the mouse cursor should be visible or not.  + *  + * @param visible whether the mouse cursor should be visible or not.  + */  + public void setCursorVisible(boolean visible) {  + if (mouseVisible != visible) {  + mouseVisible = visible;  + mouse.setCursorVisible(mouseVisible);  + }  + }  +  + /**  + * Returns the current cursor position. The position is relative to the  + * bottom-left of the screen and is in pixels.  + *  + * @return the current cursor position  + */  + public Vector2f getCursorPosition() {  + return cursorPos;  + }  +  + /**  + * Returns an array of all joysticks installed on the system.  + *  + * @return an array of all joysticks installed on the system.  + */  + public Joystick[] getJoysticks() {  + return joysticks;  + }  +  + /**  + * Adds a {@link RawInputListener} to receive raw input events.  + *  + * <p>  + * Any raw input listeners registered to this <code>InputManager</code>  + * will receive raw input events first, before they get handled  + * by the <code>InputManager</code> itself. The listeners are  + * each processed in the order they were added, e.g. FIFO.  + * <p>  + * If a raw input listener has handled the event and does not wish  + * other listeners down the list to process the event, it may set the  + * {@link InputEvent#setConsumed() consumed flag} to indicate the  + * event was consumed and shouldn't be processed any further.  + * The listener may do this either at each of the event callbacks  + * or at the {@link RawInputListener#endInput() } method.  + *  + * @param listener A listener to receive raw input events.  + *  + * @see RawInputListener  + */  + public void addRawInputListener(RawInputListener listener) {  + rawListeners.add(listener);  + rawListenerArray = null;  + }  +  + /**  + * Removes a {@link RawInputListener} so that it no longer  + * receives raw input events.  + *  + * @param listener The listener to cease receiving raw input events.  + *  + * @see InputManager#addRawInputListener(com.jme3.input.RawInputListener)  + */  + public void removeRawInputListener(RawInputListener listener) {  + rawListeners.remove(listener);  + rawListenerArray = null;  + }  +  + /**  + * Clears all {@link RawInputListener}s.  + *  + * @see InputManager#addRawInputListener(com.jme3.input.RawInputListener)  + */  + public void clearRawInputListeners() {  + rawListeners.clear();  + rawListenerArray = null;  + }  +  + private RawInputListener[] getRawListenerArray() {  + if (rawListenerArray == null)  + rawListenerArray = rawListeners.toArray(new RawInputListener[rawListeners.size()]);  + return rawListenerArray;  + }  +  + /**  + * Enable simulation of mouse events. Used for touchscreen input only.  + *  + * @param value True to enable simulation of mouse events  + */  + public void setSimulateMouse(boolean value) {  + if (touch != null) {  + touch.setSimulateMouse(value);  + }  + }  + /**  + * Returns state of simulation of mouse events. Used for touchscreen input only.  + *  + */  + public boolean getSimulateMouse() {  + if (touch != null) {  + return touch.getSimulateMouse();  + } else {  + return false;  + }  + }  +  + /**  + * Enable simulation of keyboard events. Used for touchscreen input only.  + *  + * @param value True to enable simulation of keyboard events  + */  + public void setSimulateKeyboard(boolean value) {  + if (touch != null) {  + touch.setSimulateKeyboard(value);  + }  + }  +  + private void processQueue() {  + int queueSize = inputQueue.size();  + RawInputListener[] array = getRawListenerArray();  +  + for (RawInputListener listener : array) {  + listener.beginInput();  +  + for (int j = 0; j < queueSize; j++) {  + InputEvent event = inputQueue.get(j);  + if (event.isConsumed()) {  + continue;  + }  +  + if (event instanceof MouseMotionEvent) {  + listener.onMouseMotionEvent((MouseMotionEvent) event);  + } else if (event instanceof KeyInputEvent) {  + listener.onKeyEvent((KeyInputEvent) event);  + } else if (event instanceof MouseButtonEvent) {  + listener.onMouseButtonEvent((MouseButtonEvent) event);  + } else if (event instanceof JoyAxisEvent) {  + listener.onJoyAxisEvent((JoyAxisEvent) event);  + } else if (event instanceof JoyButtonEvent) {  + listener.onJoyButtonEvent((JoyButtonEvent) event);  + } else if (event instanceof TouchEvent) {  + listener.onTouchEvent((TouchEvent) event);  + } else {  + assert false;  + }  + }  +  + listener.endInput();  + }  +  + for (int i = 0; i < queueSize; i++) {  + InputEvent event = inputQueue.get(i);  + if (event.isConsumed()) {  + continue;  + }  +  + if (event instanceof MouseMotionEvent) {  + onMouseMotionEventQueued((MouseMotionEvent) event);  + } else if (event instanceof KeyInputEvent) {  + onKeyEventQueued((KeyInputEvent) event);  + } else if (event instanceof MouseButtonEvent) {  + onMouseButtonEventQueued((MouseButtonEvent) event);  + } else if (event instanceof JoyAxisEvent) {  + onJoyAxisEventQueued((JoyAxisEvent) event);  + } else if (event instanceof JoyButtonEvent) {  + onJoyButtonEventQueued((JoyButtonEvent) event);  + } else if (event instanceof TouchEvent) {  + onTouchEventQueued((TouchEvent) event);  + } else {  + assert false;  + }  + // larynx, 2011.06.10 - flag event as reusable because  + // the android input uses a non-allocating ringbuffer which  + // needs to know when the event is not anymore in inputQueue  + // and therefor can be reused.  + event.setConsumed();  + }  +  + inputQueue.clear();  + }  +  + /**  + * Updates the <code>InputManager</code>.  + * This will query current input devices and send  + * appropriate events to registered listeners.  + *  + * @param tpf Time per frame value.  + */  + public void update(float tpf) {  + frameTPF = tpf;  +  + // Activate safemode if the TPF value is so small  + // that rounding errors are inevitable  + safeMode = tpf < 0.015f;  +  + long currentTime = keys.getInputTimeNanos();  + frameDelta = currentTime - lastUpdateTime;  +  + eventsPermitted = true;  +  + keys.update();  + mouse.update();  + if (joystick != null) {  + joystick.update();  + }  + if (touch != null) {  + touch.update();  + }  +  + eventsPermitted = false;  +  + processQueue();  + invokeUpdateActions();  +  + lastLastUpdateTime = lastUpdateTime;  + lastUpdateTime = currentTime;  + }  +  + /**  + * Dispatches touch events to touch listeners  + * @param evt The touch event to be dispatched to all onTouch listeners  + */  + public void onTouchEventQueued(TouchEvent evt) {  + ArrayList<Mapping> maps = bindings.get(TouchTrigger.touchHash(evt.getKeyCode()));  + if (maps == null) {  + return;  + }  +  + int size = maps.size();  + for (int i = size - 1; i >= 0; i--) {  + Mapping mapping = maps.get(i);  + ArrayList<InputListener> listeners = mapping.listeners;  + int listenerSize = listeners.size();  + for (int j = listenerSize - 1; j >= 0; j--) {  + InputListener listener = listeners.get(j);  + if (listener instanceof TouchListener) {  + ((TouchListener) listener).onTouch(mapping.name, evt, frameTPF);  + }  + }  + }  + }  +  + /**  + * Callback from RawInputListener. Do not use.  + */  + @Override  + public void onTouchEvent(TouchEvent evt) {  + if (!eventsPermitted) {  + throw new UnsupportedOperationException("TouchInput has raised an event at an illegal time.");  + }  + inputQueue.add(evt);  + }  +}  
diff --git a/engine/src/core/com/jme3/input/SoftTextDialogInput.java b/engine/src/core/com/jme3/input/SoftTextDialogInput.java new file mode 100644 index 0000000..d166b97 --- /dev/null +++ b/engine/src/core/com/jme3/input/SoftTextDialogInput.java 
@@ -0,0 +1,13 @@ +package com.jme3.input;  +  +import com.jme3.input.controls.SoftTextDialogInputListener;  +  +public interface SoftTextDialogInput {  +  + public static int TEXT_ENTRY_DIALOG = 0;  + public static int NUMERIC_ENTRY_DIALOG = 1;  + public static int NUMERIC_KEYPAD_DIALOG = 2;  +  + public void requestDialog(int id, String title, String initialValue, SoftTextDialogInputListener listener);  +  +}  
diff --git a/engine/src/core/com/jme3/input/TouchInput.java b/engine/src/core/com/jme3/input/TouchInput.java index 2f45b44..e69c4b3 100644 --- a/engine/src/core/com/jme3/input/TouchInput.java +++ b/engine/src/core/com/jme3/input/TouchInput.java 
@@ -73,6 +73,12 @@  * @param simulate if mouse events should be generated  */  public void setSimulateMouse(boolean simulate); +  + /** + * Get if mouse events are generated + * + */ + public boolean getSimulateMouse();    /**  * Set if keyboard events should be generated 
diff --git a/engine/src/core/com/jme3/input/controls/SoftTextDialogInputListener.java b/engine/src/core/com/jme3/input/controls/SoftTextDialogInputListener.java new file mode 100644 index 0000000..d33844d --- /dev/null +++ b/engine/src/core/com/jme3/input/controls/SoftTextDialogInputListener.java 
@@ -0,0 +1,44 @@ +/* + * Copyright (c) 2009-2010 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.input.controls; + +/** + * + * @author potterec (aka iwgeric) + */ +public interface SoftTextDialogInputListener { + + public static int COMPLETE = 0; + public static int CANCEL = 1; + + public void onSoftText(int action, String text); +} 
diff --git a/engine/src/core/com/jme3/math/Transform.java b/engine/src/core/com/jme3/math/Transform.java index 7ccd847..3573d8c 100644 --- a/engine/src/core/com/jme3/math/Transform.java +++ b/engine/src/core/com/jme3/math/Transform.java 
@@ -1,318 +1,318 @@ -/*  - * Copyright (c) 2009-2010 jMonkeyEngine  - * All rights reserved.  - *  - * Redistribution and use in source and binary forms, with or without  - * modification, are permitted provided that the following conditions are  - * met:  - *  - * * Redistributions of source code must retain the above copyright  - * notice, this list of conditions and the following disclaimer.  - *  - * * Redistributions in binary form must reproduce the above copyright  - * notice, this list of conditions and the following disclaimer in the  - * documentation and/or other materials provided with the distribution.  - *  - * * Neither the name of 'jMonkeyEngine' nor the names of its contributors  - * may be used to endorse or promote products derived from this software  - * without specific prior written permission.  - *  - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED  - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR  - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR  - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,  - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,  - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR  - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF  - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING  - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS  - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  - */  -  -package com.jme3.math;  -  -import com.jme3.export.*;  -import java.io.IOException;  -  -/**  - * Started Date: Jul 16, 2004<br><br>  - * Represents a translation, rotation and scale in one object.  - *  - * @author Jack Lindamood  - * @author Joshua Slack  - */  -public final class Transform implements Savable, Cloneable, java.io.Serializable {  -  - static final long serialVersionUID = 1;  -  - public static final Transform IDENTITY = new Transform();  -  - private Quaternion rot = new Quaternion();  - private Vector3f translation = new Vector3f();  - private Vector3f scale = new Vector3f(1,1,1);  -  - public Transform(Vector3f translation, Quaternion rot){  - this.translation.set(translation);  - this.rot.set(rot);  - }  -  - public Transform(Vector3f translation, Quaternion rot, Vector3f scale){  - this(translation, rot);  - this.scale.set(scale);  - }  -  - public Transform(Vector3f translation){  - this(translation, Quaternion.IDENTITY);  - }  -  - public Transform(Quaternion rot){  - this(Vector3f.ZERO, rot);  - }  -  - public Transform(){  - this(Vector3f.ZERO, Quaternion.IDENTITY);  - }  -  - /**  - * Sets this rotation to the given Quaternion value.  - * @param rot The new rotation for this matrix.  - * @return this  - */  - public Transform setRotation(Quaternion rot) {  - this.rot.set(rot);  - return this;  - }  -  - /**  - * Sets this translation to the given value.  - * @param trans The new translation for this matrix.  - * @return this  - */  - public Transform setTranslation(Vector3f trans) {  - this.translation.set(trans);  - return this;  - }  -  - /**  - * Return the translation vector in this matrix.  - * @return translation vector.  - */  - public Vector3f getTranslation() {  - return translation;  - }  -  - /**  - * Sets this scale to the given value.  - * @param scale The new scale for this matrix.  - * @return this  - */  - public Transform setScale(Vector3f scale) {  - this.scale.set(scale);  - return this;  - }  -  - /**  - * Sets this scale to the given value.  - * @param scale The new scale for this matrix.  - * @return this  - */  - public Transform setScale(float scale) {  - this.scale.set(scale, scale, scale);  - return this;  - }  -  - /**  - * Return the scale vector in this matrix.  - * @return scale vector.  - */  - public Vector3f getScale() {  - return scale;  - }  -  - /**  - * Stores this translation value into the given vector3f. If trans is null, a new vector3f is created to  - * hold the value. The value, once stored, is returned.  - * @param trans The store location for this matrix's translation.  - * @return The value of this matrix's translation.  - */  - public Vector3f getTranslation(Vector3f trans) {  - if (trans==null) trans=new Vector3f();  - trans.set(this.translation);  - return trans;  - }  -  - /**  - * Stores this rotation value into the given Quaternion. If quat is null, a new Quaternion is created to  - * hold the value. The value, once stored, is returned.  - * @param quat The store location for this matrix's rotation.  - * @return The value of this matrix's rotation.  - */  - public Quaternion getRotation(Quaternion quat) {  - if (quat==null) quat=new Quaternion();  - quat.set(rot);  - return quat;  - }  -  - /**  - * Return the rotation quaternion in this matrix.  - * @return rotation quaternion.  - */  - public Quaternion getRotation() {  - return rot;  - }  -  - /**  - * Stores this scale value into the given vector3f. If scale is null, a new vector3f is created to  - * hold the value. The value, once stored, is returned.  - * @param scale The store location for this matrix's scale.  - * @return The value of this matrix's scale.  - */  - public Vector3f getScale(Vector3f scale) {  - if (scale==null) scale=new Vector3f();  - scale.set(this.scale);  - return scale;  - }  -  - /**  - * Sets this matrix to the interpolation between the first matrix and the second by delta amount.  - * @param t1 The begining transform.  - * @param t2 The ending transform.  - * @param delta An amount between 0 and 1 representing how far to interpolate from t1 to t2.  - */  - public void interpolateTransforms(Transform t1, Transform t2, float delta) {  - this.rot.slerp(t1.rot,t2.rot,delta);  - this.translation.interpolate(t1.translation,t2.translation,delta);  - this.scale.interpolate(t1.scale,t2.scale,delta);  - }  -  - /**  - * Changes the values of this matrix acording to it's parent. Very similar to the concept of Node/Spatial transforms.  - * @param parent The parent matrix.  - * @return This matrix, after combining.  - */  - public Transform combineWithParent(Transform parent) {  - scale.multLocal(parent.scale);  -// rot.multLocal(parent.rot);  - parent.rot.mult(rot, rot);  -  - // This here, is evil code  -// parent  -// .rot  -// .multLocal(translation)  -// .multLocal(parent.scale)  -// .addLocal(parent.translation);  -  - translation.multLocal(parent.scale);  - parent  - .rot  - .multLocal(translation)  - .addLocal(parent.translation);  - return this;  - }  -  - /**  - * Sets this matrix's translation to the given x,y,z values.  - * @param x This matrix's new x translation.  - * @param y This matrix's new y translation.  - * @param z This matrix's new z translation.  - * @return this  - */  - public Transform setTranslation(float x,float y, float z) {  - translation.set(x,y,z);  - return this;  - }  -  - /**  - * Sets this matrix's scale to the given x,y,z values.  - * @param x This matrix's new x scale.  - * @param y This matrix's new y scale.  - * @param z This matrix's new z scale.  - * @return this  - */  - public Transform setScale(float x, float y, float z) {  - scale.set(x,y,z);  - return this;  - }  -  - public Vector3f transformVector(final Vector3f in, Vector3f store){  - if (store == null)  - store = new Vector3f();  -  - // multiply with scale first, then rotate, finally translate (cf.  - // Eberly)  - return rot.mult(store.set(in).multLocal(scale), store).addLocal(translation);  - }  -  - public Vector3f transformInverseVector(final Vector3f in, Vector3f store){  - if (store == null)  - store = new Vector3f();  -  - // The author of this code should look above and take the inverse of that  - // But for some reason, they didnt ..  -// in.subtract(translation, store).divideLocal(scale);  -// rot.inverse().mult(store, store);  -  - in.subtract(translation, store);  - rot.inverse().mult(store, store);  - store.divideLocal(scale);  -  - return store;  - }  -  - /**  - * Loads the identity. Equal to translation=1,1,1 scale=0,0,0 rot=0,0,0,1.  - */  - public void loadIdentity() {  - translation.set(0,0,0);  - scale.set(1,1,1);  - rot.set(0,0,0,1);  - }  -  - @Override  - public String toString(){  - return getClass().getSimpleName() + "[ " + translation.x + ", " + translation.y + ", " + translation.z + "]\n"  - + "[ " + rot.x + ", " + rot.y + ", " + rot.z + ", " + rot.w + "]\n"  - + "[ " + scale.x + " , " + scale.y + ", " + scale.z + "]";  - }  -  - /**  - * Sets this matrix to be equal to the given matrix.  - * @param matrixQuat The matrix to be equal to.  - * @return this  - */  - public Transform set(Transform matrixQuat) {  - this.translation.set(matrixQuat.translation);  - this.rot.set(matrixQuat.rot);  - this.scale.set(matrixQuat.scale);  - return this;  - }  -  - public void write(JmeExporter e) throws IOException {  - OutputCapsule capsule = e.getCapsule(this);  - capsule.write(rot, "rot", new Quaternion());  - capsule.write(translation, "translation", Vector3f.ZERO);  - capsule.write(scale, "scale", Vector3f.UNIT_XYZ);  - }  -  - public void read(JmeImporter e) throws IOException {  - InputCapsule capsule = e.getCapsule(this);  -  - rot = (Quaternion)capsule.readSavable("rot", new Quaternion());  - translation = (Vector3f)capsule.readSavable("translation", Vector3f.ZERO);  - scale = (Vector3f)capsule.readSavable("scale", Vector3f.UNIT_XYZ);  - }  -  - @Override  - public Transform clone() {  - try {  - Transform tq = (Transform) super.clone();  - tq.rot = rot.clone();  - tq.scale = scale.clone();  - tq.translation = translation.clone();  - return tq;  - } catch (CloneNotSupportedException e) {  - throw new AssertionError();  - }  - }  -}  +/* + * Copyright (c) 2009-2010 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jme3.math; + +import com.jme3.export.*; +import java.io.IOException; + +/** + * Started Date: Jul 16, 2004<br><br> + * Represents a translation, rotation and scale in one object. + *  + * @author Jack Lindamood + * @author Joshua Slack + */ +public final class Transform implements Savable, Cloneable, java.io.Serializable { + + static final long serialVersionUID = 1; + + public static final Transform IDENTITY = new Transform(); + + private Quaternion rot = new Quaternion(); + private Vector3f translation = new Vector3f(); + private Vector3f scale = new Vector3f(1,1,1); + + public Transform(Vector3f translation, Quaternion rot){ + this.translation.set(translation); + this.rot.set(rot); + } +  + public Transform(Vector3f translation, Quaternion rot, Vector3f scale){ + this(translation, rot); + this.scale.set(scale); + } + + public Transform(Vector3f translation){ + this(translation, Quaternion.IDENTITY); + } + + public Transform(Quaternion rot){ + this(Vector3f.ZERO, rot); + } + + public Transform(){ + this(Vector3f.ZERO, Quaternion.IDENTITY); + } + + /** + * Sets this rotation to the given Quaternion value. + * @param rot The new rotation for this matrix. + * @return this + */ + public Transform setRotation(Quaternion rot) { + this.rot.set(rot); + return this; + } + + /** + * Sets this translation to the given value. + * @param trans The new translation for this matrix. + * @return this + */ + public Transform setTranslation(Vector3f trans) { + this.translation.set(trans); + return this; + } + + /** + * Return the translation vector in this matrix. + * @return translation vector. + */ + public Vector3f getTranslation() { + return translation; + } + + /** + * Sets this scale to the given value. + * @param scale The new scale for this matrix. + * @return this + */ + public Transform setScale(Vector3f scale) { + this.scale.set(scale); + return this; + } + + /** + * Sets this scale to the given value. + * @param scale The new scale for this matrix. + * @return this + */ + public Transform setScale(float scale) { + this.scale.set(scale, scale, scale); + return this; + } + + /** + * Return the scale vector in this matrix. + * @return scale vector. + */ + public Vector3f getScale() { + return scale; + } + + /** + * Stores this translation value into the given vector3f. If trans is null, a new vector3f is created to + * hold the value. The value, once stored, is returned. + * @param trans The store location for this matrix's translation. + * @return The value of this matrix's translation. + */ + public Vector3f getTranslation(Vector3f trans) { + if (trans==null) trans=new Vector3f(); + trans.set(this.translation); + return trans; + } + + /** + * Stores this rotation value into the given Quaternion. If quat is null, a new Quaternion is created to + * hold the value. The value, once stored, is returned. + * @param quat The store location for this matrix's rotation. + * @return The value of this matrix's rotation. + */ + public Quaternion getRotation(Quaternion quat) { + if (quat==null) quat=new Quaternion(); + quat.set(rot); + return quat; + } +  + /** + * Return the rotation quaternion in this matrix. + * @return rotation quaternion. + */ + public Quaternion getRotation() { + return rot; + }  +  + /** + * Stores this scale value into the given vector3f. If scale is null, a new vector3f is created to + * hold the value. The value, once stored, is returned. + * @param scale The store location for this matrix's scale. + * @return The value of this matrix's scale. + */ + public Vector3f getScale(Vector3f scale) { + if (scale==null) scale=new Vector3f(); + scale.set(this.scale); + return scale; + } + + /** + * Sets this matrix to the interpolation between the first matrix and the second by delta amount. + * @param t1 The begining transform. + * @param t2 The ending transform. + * @param delta An amount between 0 and 1 representing how far to interpolate from t1 to t2. + */ + public void interpolateTransforms(Transform t1, Transform t2, float delta) { + this.rot.slerp(t1.rot,t2.rot,delta); + this.translation.interpolate(t1.translation,t2.translation,delta); + this.scale.interpolate(t1.scale,t2.scale,delta); + } + + /** + * Changes the values of this matrix acording to it's parent. Very similar to the concept of Node/Spatial transforms. + * @param parent The parent matrix. + * @return This matrix, after combining. + */ + public Transform combineWithParent(Transform parent) { + scale.multLocal(parent.scale); +// rot.multLocal(parent.rot); + parent.rot.mult(rot, rot); + + // This here, is evil code +// parent +// .rot +// .multLocal(translation) +// .multLocal(parent.scale) +// .addLocal(parent.translation); + + translation.multLocal(parent.scale); + parent + .rot + .multLocal(translation) + .addLocal(parent.translation); + return this; + } + + /** + * Sets this matrix's translation to the given x,y,z values. + * @param x This matrix's new x translation. + * @param y This matrix's new y translation. + * @param z This matrix's new z translation. + * @return this + */ + public Transform setTranslation(float x,float y, float z) { + translation.set(x,y,z); + return this; + } + + /** + * Sets this matrix's scale to the given x,y,z values. + * @param x This matrix's new x scale. + * @param y This matrix's new y scale. + * @param z This matrix's new z scale. + * @return this + */ + public Transform setScale(float x, float y, float z) { + scale.set(x,y,z); + return this; + } + + public Vector3f transformVector(final Vector3f in, Vector3f store){ + if (store == null) + store = new Vector3f(); + + // multiply with scale first, then rotate, finally translate (cf. + // Eberly) + return rot.mult(store.set(in).multLocal(scale), store).addLocal(translation); + } + + public Vector3f transformInverseVector(final Vector3f in, Vector3f store){ + if (store == null) + store = new Vector3f(); + + // The author of this code should look above and take the inverse of that + // But for some reason, they didnt .. +// in.subtract(translation, store).divideLocal(scale); +// rot.inverse().mult(store, store); + + in.subtract(translation, store); + rot.inverse().mult(store, store); + store.divideLocal(scale); + + return store; + } + + /** + * Loads the identity. Equal to translation=0,0,0 scale=1,1,1 rot=0,0,0,1. + */ + public void loadIdentity() { + translation.set(0,0,0); + scale.set(1,1,1); + rot.set(0,0,0,1); + } + + @Override + public String toString(){ + return getClass().getSimpleName() + "[ " + translation.x + ", " + translation.y + ", " + translation.z + "]\n" + + "[ " + rot.x + ", " + rot.y + ", " + rot.z + ", " + rot.w + "]\n" + + "[ " + scale.x + " , " + scale.y + ", " + scale.z + "]"; + } + + /** + * Sets this matrix to be equal to the given matrix. + * @param matrixQuat The matrix to be equal to. + * @return this + */ + public Transform set(Transform matrixQuat) { + this.translation.set(matrixQuat.translation); + this.rot.set(matrixQuat.rot); + this.scale.set(matrixQuat.scale); + return this; + } + + public void write(JmeExporter e) throws IOException { + OutputCapsule capsule = e.getCapsule(this); + capsule.write(rot, "rot", new Quaternion()); + capsule.write(translation, "translation", Vector3f.ZERO); + capsule.write(scale, "scale", Vector3f.UNIT_XYZ); + } + + public void read(JmeImporter e) throws IOException { + InputCapsule capsule = e.getCapsule(this); +  + rot = (Quaternion)capsule.readSavable("rot", new Quaternion()); + translation = (Vector3f)capsule.readSavable("translation", Vector3f.ZERO); + scale = (Vector3f)capsule.readSavable("scale", Vector3f.UNIT_XYZ); + } +  + @Override + public Transform clone() { + try { + Transform tq = (Transform) super.clone(); + tq.rot = rot.clone(); + tq.scale = scale.clone(); + tq.translation = translation.clone(); + return tq; + } catch (CloneNotSupportedException e) { + throw new AssertionError(); + } + } +} 
diff --git a/engine/src/core/com/jme3/post/Filter.java b/engine/src/core/com/jme3/post/Filter.java index ef9a6ff..12babf5 100644 --- a/engine/src/core/com/jme3/post/Filter.java +++ b/engine/src/core/com/jme3/post/Filter.java 
@@ -40,6 +40,7 @@  import com.jme3.renderer.ViewPort;   import com.jme3.texture.FrameBuffer;   import com.jme3.texture.Image.Format;  +import com.jme3.texture.Texture;   import com.jme3.texture.Texture2D;   import java.io.IOException;   import java.util.Collection;  @@ -236,7 +237,7 @@  * cleanup this filter   * @param r   */  - protected final void cleanup(Renderer r) {  + protected final void cleanup(Renderer r) {   processor = null;   if (defaultPass != null) {   defaultPass.cleanup(r);  @@ -269,8 +270,6 @@  protected void cleanUpFilter(Renderer r) {   }    - ;  -   /**   * Must return the material used for this filter.   * this method is called every frame.  @@ -278,6 +277,14 @@  * @return the material used for this filter.   */   protected abstract Material getMaterial();  +  + /**  + * Override if you want to do something special with the depth texture;  + * @param depthTexture  + */  + protected void setDepthTexture(Texture depthTexture){  + getMaterial().setTexture("DepthTexture", depthTexture);  + }     /**   * Override this method if you want to make a pre pass, before the actual rendering of the frame  
diff --git a/engine/src/core/com/jme3/post/FilterPostProcessor.java b/engine/src/core/com/jme3/post/FilterPostProcessor.java index 2e48f0f..d97e8e9 100644 --- a/engine/src/core/com/jme3/post/FilterPostProcessor.java +++ b/engine/src/core/com/jme3/post/FilterPostProcessor.java 
@@ -77,7 +77,7 @@  private int originalHeight;   private int lastFilterIndex = -1;   private boolean cameraInit = false;  -  +   /**   * Create a FilterProcessor   * @param assetManager the assetManager  @@ -98,8 +98,7 @@  * @param filter the filter to add   */   public void addFilter(Filter filter) {  - filters.add(filter);  - filter.setProcessor(this);  + filters.add(filter);     if (isInitialized()) {   initFilter(filter, viewPort);  @@ -148,14 +147,17 @@  * @param vp   */   private void initFilter(Filter filter, ViewPort vp) {  - filter.init(assetManager, renderManager, vp, width, height);  + filter.setProcessor(this);   if (filter.isRequiresDepthTexture()) {  - if (!computeDepth && renderFrameBuffer != null) {  + if (!computeDepth && renderFrameBuffer != null) {   depthTexture = new Texture2D(width, height, Format.Depth24);   renderFrameBuffer.setDepthTexture(depthTexture);   }   computeDepth = true;  - filter.getMaterial().setTexture("DepthTexture", depthTexture);  + filter.init(assetManager, renderManager, vp, width, height);  + filter.setDepthTexture(depthTexture);  + } else {  + filter.init(assetManager, renderManager, vp, width, height);   }   }    @@ -281,9 +283,9 @@  } else if (renderFrameBufferMS != null) {   sceneBuffer = renderFrameBufferMS;   }  - renderFilterChain(renderer, sceneBuffer);  + renderFilterChain(renderer, sceneBuffer);   renderer.setFrameBuffer(outputBuffer);  -  +   //viewport can be null if no filters are enabled   if (viewPort != null) {   renderManager.setCamera(viewPort.getCamera(), false);  @@ -356,8 +358,11 @@  //reseting the viewport camera viewport to its initial value   viewPort.getCamera().resize(originalWidth, originalHeight, true);   viewPort.getCamera().setViewPort(left, right, bottom, top);  - viewPort.setOutputFrameBuffer(outputBuffer);  + viewPort.setOutputFrameBuffer(outputBuffer);   viewPort = null;  + for (Filter filter : filters) {  + filter.cleanup(renderer);  + }   }     }  @@ -484,7 +489,7 @@  * For internal use only<br>   * returns the depth texture of the scene   * @return  - */  + */   public Texture2D getDepthTexture() {   return depthTexture;   }  
diff --git a/engine/src/core/com/jme3/renderer/RenderManager.java b/engine/src/core/com/jme3/renderer/RenderManager.java index 1d58d22..a0f5fdf 100644 --- a/engine/src/core/com/jme3/renderer/RenderManager.java +++ b/engine/src/core/com/jme3/renderer/RenderManager.java 
@@ -1,1170 +1,1178 @@ -/* - * Copyright (c) 2009-2012 jMonkeyEngine - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of 'jMonkeyEngine' nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.jme3.renderer; - -import com.jme3.material.Material; -import com.jme3.material.MaterialDef; -import com.jme3.material.RenderState; -import com.jme3.material.Technique; -import com.jme3.math.*; -import com.jme3.post.SceneProcessor; -import com.jme3.renderer.queue.GeometryList; -import com.jme3.renderer.queue.RenderQueue; -import com.jme3.renderer.queue.RenderQueue.Bucket; -import com.jme3.renderer.queue.RenderQueue.ShadowMode; -import com.jme3.scene.*; -import com.jme3.shader.Uniform; -import com.jme3.shader.UniformBinding; -import com.jme3.shader.VarType; -import com.jme3.system.NullRenderer; -import com.jme3.system.Timer; -import com.jme3.util.IntMap.Entry; -import com.jme3.util.TempVars; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.logging.Logger; - -/** - * <code>RenderManager</code> is a high-level rendering interface that is - * above the Renderer implementation. RenderManager takes care - * of rendering the scene graphs attached to each viewport and - * handling SceneProcessors. - * - * @see SceneProcessor - * @see ViewPort - * @see Spatial - */ -public class RenderManager { - - private static final Logger logger = Logger.getLogger(RenderManager.class.getName()); -  - private Renderer renderer; - private Timer timer; - private ArrayList<ViewPort> preViewPorts = new ArrayList<ViewPort>(); - private ArrayList<ViewPort> viewPorts = new ArrayList<ViewPort>(); - private ArrayList<ViewPort> postViewPorts = new ArrayList<ViewPort>(); - private Camera prevCam = null; - private Material forcedMaterial = null; - private String forcedTechnique = null; - private RenderState forcedRenderState = null; - private boolean shader; - private int viewX, viewY, viewWidth, viewHeight; - private float near, far; - private Matrix4f orthoMatrix = new Matrix4f(); - private Matrix4f viewMatrix = new Matrix4f(); - private Matrix4f projMatrix = new Matrix4f(); - private Matrix4f viewProjMatrix = new Matrix4f(); - private Matrix4f worldMatrix = new Matrix4f(); - private Vector3f camUp = new Vector3f(), - camLeft = new Vector3f(), - camDir = new Vector3f(), - camLoc = new Vector3f(); - //temp technique - private String tmpTech; - private boolean handleTranlucentBucket = true; - - /** - * Create a high-level rendering interface over the - * low-level rendering interface. - * @param renderer - */ - public RenderManager(Renderer renderer) { - this.renderer = renderer; - //this.shader = renderer.getCaps().contains(Caps.GLSL100); - } - - /** - * Returns the pre ViewPort with the given name. - *  - * @param viewName The name of the pre ViewPort to look up - * @return The ViewPort, or null if not found. - *  - * @see #createPreView(java.lang.String, com.jme3.renderer.Camera)  - */ - public ViewPort getPreView(String viewName) { - for (int i = 0; i < preViewPorts.size(); i++) { - if (preViewPorts.get(i).getName().equals(viewName)) { - return preViewPorts.get(i); - } - } - return null; - } - - /** - * Removes the specified pre ViewPort. - *  - * @param view The pre ViewPort to remove - * @return True if the ViewPort was removed successfully. - *  - * @see #createPreView(java.lang.String, com.jme3.renderer.Camera)  - */ - public boolean removePreView(ViewPort view) { - return preViewPorts.remove(view); - } - - /** - * Returns the main ViewPort with the given name. - *  - * @param viewName The name of the main ViewPort to look up - * @return The ViewPort, or null if not found. - *  - * @see #createMainView(java.lang.String, com.jme3.renderer.Camera)  - */ - public ViewPort getMainView(String viewName) { - for (int i = 0; i < viewPorts.size(); i++) { - if (viewPorts.get(i).getName().equals(viewName)) { - return viewPorts.get(i); - } - } - return null; - } - - /** - * Removes the main ViewPort with the specified name. - *  - * @param viewName The main ViewPort name to remove - * @return True if the ViewPort was removed successfully. - *  - * @see #createMainView(java.lang.String, com.jme3.renderer.Camera)  - */ - public boolean removeMainView(String viewName) { - for (int i = 0; i < viewPorts.size(); i++) { - if (viewPorts.get(i).getName().equals(viewName)) { - viewPorts.remove(i); - return true; - } - } - return false; - } - - /** - * Removes the specified main ViewPort. - *  - * @param view The main ViewPort to remove - * @return True if the ViewPort was removed successfully. - *  - * @see #createMainView(java.lang.String, com.jme3.renderer.Camera)  - */ - public boolean removeMainView(ViewPort view) { - return viewPorts.remove(view); - } - - /** - * Returns the post ViewPort with the given name. - *  - * @param viewName The name of the post ViewPort to look up - * @return The ViewPort, or null if not found. - *  - * @see #createPostView(java.lang.String, com.jme3.renderer.Camera)  - */ - public ViewPort getPostView(String viewName) { - for (int i = 0; i < postViewPorts.size(); i++) { - if (postViewPorts.get(i).getName().equals(viewName)) { - return postViewPorts.get(i); - } - } - return null; - } - - /** - * Removes the post ViewPort with the specified name. - *  - * @param viewName The post ViewPort name to remove - * @return True if the ViewPort was removed successfully. - *  - * @see #createPostView(java.lang.String, com.jme3.renderer.Camera)  - */ - public boolean removePostView(String viewName) { - for (int i = 0; i < postViewPorts.size(); i++) { - if (postViewPorts.get(i).getName().equals(viewName)) { - postViewPorts.remove(i); - - return true; - } - } - return false; - } - - /** - * Removes the specified post ViewPort. - *  - * @param view The post ViewPort to remove - * @return True if the ViewPort was removed successfully. - *  - * @see #createPostView(java.lang.String, com.jme3.renderer.Camera)  - */ - public boolean removePostView(ViewPort view) { - return postViewPorts.remove(view); - } - - /** - * Returns a read-only list of all pre ViewPorts - * @return a read-only list of all pre ViewPorts - * @see #createPreView(java.lang.String, com.jme3.renderer.Camera)  - */ - public List<ViewPort> getPreViews() { - return Collections.unmodifiableList(preViewPorts); - } - - /** - * Returns a read-only list of all main ViewPorts - * @return a read-only list of all main ViewPorts - * @see #createMainView(java.lang.String, com.jme3.renderer.Camera)  - */ - public List<ViewPort> getMainViews() { - return Collections.unmodifiableList(viewPorts); - } - - /** - * Returns a read-only list of all post ViewPorts - * @return a read-only list of all post ViewPorts - * @see #createPostView(java.lang.String, com.jme3.renderer.Camera)  - */ - public List<ViewPort> getPostViews() { - return Collections.unmodifiableList(postViewPorts); - } - - /** - * Creates a new pre ViewPort, to display the given camera's content. - * <p> - * The view will be processed before the main and post viewports. - */ - public ViewPort createPreView(String viewName, Camera cam) { - ViewPort vp = new ViewPort(viewName, cam); - preViewPorts.add(vp); - return vp; - } - - /** - * Creates a new main ViewPort, to display the given camera's content. - * <p> - * The view will be processed before the post viewports but after - * the pre viewports. - */ - public ViewPort createMainView(String viewName, Camera cam) { - ViewPort vp = new ViewPort(viewName, cam); - viewPorts.add(vp); - return vp; - } - - /** - * Creates a new post ViewPort, to display the given camera's content. - * <p> - * The view will be processed after the pre and main viewports. - */ - public ViewPort createPostView(String viewName, Camera cam) { - ViewPort vp = new ViewPort(viewName, cam); - postViewPorts.add(vp); - return vp; - } - - private void notifyReshape(ViewPort vp, int w, int h) { - List<SceneProcessor> processors = vp.getProcessors(); - for (SceneProcessor proc : processors) { - if (!proc.isInitialized()) { - proc.initialize(this, vp); - } else { - proc.reshape(vp, w, h); - } - } - } - - /** - * Internal use only. - * Updates the resolution of all on-screen cameras to match - * the given width and height. - */ - public void notifyReshape(int w, int h) { - for (ViewPort vp : preViewPorts) { - if (vp.getOutputFrameBuffer() == null) { - Camera cam = vp.getCamera(); - cam.resize(w, h, true); - } - notifyReshape(vp, w, h); - } - for (ViewPort vp : viewPorts) { - if (vp.getOutputFrameBuffer() == null) { - Camera cam = vp.getCamera(); - cam.resize(w, h, true); - } - notifyReshape(vp, w, h); - } - for (ViewPort vp : postViewPorts) { - if (vp.getOutputFrameBuffer() == null) { - Camera cam = vp.getCamera(); - cam.resize(w, h, true); - } - notifyReshape(vp, w, h); - } - } - - /** - * Internal use only. - * Updates the given list of uniforms with {@link UniformBinding uniform bindings} - * based on the current world state. - */ - public void updateUniformBindings(List<Uniform> params) { - // assums worldMatrix is properly set. - TempVars vars = TempVars.get(); - - Matrix4f tempMat4 = vars.tempMat4; - Matrix3f tempMat3 = vars.tempMat3; - Vector2f tempVec2 = vars.vect2d; - Quaternion tempVec4 = vars.quat1; - - for (int i = 0; i < params.size(); i++) { - Uniform u = params.get(i); - switch (u.getBinding()) { - case WorldMatrix: - u.setValue(VarType.Matrix4, worldMatrix); - break; - case ViewMatrix: - u.setValue(VarType.Matrix4, viewMatrix); - break; - case ProjectionMatrix: - u.setValue(VarType.Matrix4, projMatrix); - break; - case ViewProjectionMatrix: - u.setValue(VarType.Matrix4, viewProjMatrix); - break; - case WorldViewMatrix: - tempMat4.set(viewMatrix); - tempMat4.multLocal(worldMatrix); - u.setValue(VarType.Matrix4, tempMat4); - break; - case NormalMatrix: - tempMat4.set(viewMatrix); - tempMat4.multLocal(worldMatrix); - tempMat4.toRotationMatrix(tempMat3); - tempMat3.invertLocal(); - tempMat3.transposeLocal(); - u.setValue(VarType.Matrix3, tempMat3); - break; - case WorldViewProjectionMatrix: - tempMat4.set(viewProjMatrix); - tempMat4.multLocal(worldMatrix); - u.setValue(VarType.Matrix4, tempMat4); - break; - case WorldMatrixInverse: - tempMat4.multLocal(worldMatrix); - tempMat4.invertLocal(); - u.setValue(VarType.Matrix4, tempMat4); - break; - case ViewMatrixInverse: - tempMat4.set(viewMatrix); - tempMat4.invertLocal(); - u.setValue(VarType.Matrix4, tempMat4); - break; - case ProjectionMatrixInverse: - tempMat4.set(projMatrix); - tempMat4.invertLocal(); - u.setValue(VarType.Matrix4, tempMat4); - break; - case ViewProjectionMatrixInverse: - tempMat4.set(viewProjMatrix); - tempMat4.invertLocal(); - u.setValue(VarType.Matrix4, tempMat4); - break; - case WorldViewMatrixInverse: - tempMat4.set(viewMatrix); - tempMat4.multLocal(worldMatrix); - tempMat4.invertLocal(); - u.setValue(VarType.Matrix4, tempMat4); - break; - case NormalMatrixInverse: - tempMat4.set(viewMatrix); - tempMat4.multLocal(worldMatrix); - tempMat4.toRotationMatrix(tempMat3); - tempMat3.invertLocal(); - tempMat3.transposeLocal(); - tempMat3.invertLocal(); - u.setValue(VarType.Matrix3, tempMat3); - break; - case WorldViewProjectionMatrixInverse: - tempMat4.set(viewProjMatrix); - tempMat4.multLocal(worldMatrix); - tempMat4.invertLocal(); - u.setValue(VarType.Matrix4, tempMat4); - break; - case ViewPort: - tempVec4.set(viewX, viewY, viewWidth, viewHeight); - u.setValue(VarType.Vector4, tempVec4); - break; - case Resolution: - tempVec2.set(viewWidth, viewHeight); - u.setValue(VarType.Vector2, tempVec2); - break; - case Aspect: - float aspect = ((float) viewWidth) / viewHeight; - u.setValue(VarType.Float, aspect); - break; - case FrustumNearFar: - tempVec2.set(near, far); - u.setValue(VarType.Vector2, tempVec2); - break; - case CameraPosition: - u.setValue(VarType.Vector3, camLoc); - break; - case CameraDirection: - u.setValue(VarType.Vector3, camDir); - break; - case CameraLeft: - u.setValue(VarType.Vector3, camLeft); - break; - case CameraUp: - u.setValue(VarType.Vector3, camUp); - break; - case Time: - u.setValue(VarType.Float, timer.getTimeInSeconds()); - break; - case Tpf: - u.setValue(VarType.Float, timer.getTimePerFrame()); - break; - case FrameRate: - u.setValue(VarType.Float, timer.getFrameRate()); - break; - } - } - - vars.release(); - } - - /** - * Set the material to use to render all future objects. - * This overrides the material set on the geometry and renders - * with the provided material instead. - * Use null to clear the material and return renderer to normal - * functionality. - * @param mat The forced material to set, or null to return to normal - */ - public void setForcedMaterial(Material mat) { - forcedMaterial = mat; - } - - /** - * Returns the forced render state previously set with  - * {@link #setForcedRenderState(com.jme3.material.RenderState) }. - * @return the forced render state - */ - public RenderState getForcedRenderState() { - return forcedRenderState; - } - - /** - * Set the render state to use for all future objects. - * This overrides the render state set on the material and instead - * forces this render state to be applied for all future materials - * rendered. Set to null to return to normal functionality. - *  - * @param forcedRenderState The forced render state to set, or null - * to return to normal - */ - public void setForcedRenderState(RenderState forcedRenderState) { - this.forcedRenderState = forcedRenderState; - } - - /** - * Set the timer that should be used to query the time based - * {@link UniformBinding}s for material world parameters. - *  - * @param timer The timer to query time world parameters - */ - public void setTimer(Timer timer) { - this.timer = timer; - } - - /** - * Returns the forced technique name set. - *  - * @return the forced technique name set. - *  - * @see #setForcedTechnique(java.lang.String)  - */ - public String getForcedTechnique() { - return forcedTechnique; - } - - /** - * Sets the forced technique to use when rendering geometries. - * <p> - * If the specified technique name is available on the geometry's - * material, then it is used, otherwise, the  - * {@link #setForcedMaterial(com.jme3.material.Material) forced material} is used. - * If a forced material is not set and the forced technique name cannot - * be found on the material, the geometry will <em>not</em> be rendered. - *  - * @param forcedTechnique The forced technique name to use, set to null - * to return to normal functionality. - *  - * @see #renderGeometry(com.jme3.scene.Geometry)  - */ - public void setForcedTechnique(String forcedTechnique) { - this.forcedTechnique = forcedTechnique; - } - - /** - * Enable or disable alpha-to-coverage.  - * <p> - * When alpha to coverage is enabled and the renderer implementation - * supports it, then alpha blending will be replaced with alpha dissolve - * if multi-sampling is also set on the renderer. - * This feature allows avoiding of alpha blending artifacts due to - * lack of triangle-level back-to-front sorting. - *  - * @param value True to enable alpha-to-coverage, false otherwise. - */ - public void setAlphaToCoverage(boolean value) { - renderer.setAlphaToCoverage(value); - } - - /** - * True if the translucent bucket should automatically be rendered - * by the RenderManager. - *  - * @return Whether or not the translucent bucket is rendered. - *  - * @see #setHandleTranslucentBucket(boolean)  - */ - public boolean isHandleTranslucentBucket() { - return handleTranlucentBucket; - } - - /** - * Enable or disable rendering of the  - * {@link Bucket#Translucent translucent bucket} - * by the RenderManager. The default is enabled. - *  - * @param handleTranslucentBucket Whether or not the translucent bucket should - * be rendered. - */ - public void setHandleTranslucentBucket(boolean handleTranslucentBucket) { - this.handleTranlucentBucket = handleTranslucentBucket; - } - - /** - * Internal use only. Sets the world matrix to use for future - * rendering. This has no effect unless objects are rendered manually - * using {@link Material#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager) }. - * Using {@link #renderGeometry(com.jme3.scene.Geometry) } will  - * override this value. - *  - * @param mat The world matrix to set - */ - public void setWorldMatrix(Matrix4f mat) { - if (shader) { - worldMatrix.set(mat); - } else { - renderer.setWorldMatrix(mat); - } - } - - /** - * Renders the given geometry. - * <p> - * First the proper world matrix is set, if  - * the geometry's {@link Geometry#setIgnoreTransform(boolean) ignore transform} - * feature is enabled, the identity world matrix is used, otherwise, the  - * geometry's {@link Geometry#getWorldMatrix() world transform matrix} is used.  - * <p> - * Once the world matrix is applied, the proper material is chosen for rendering. - * If a {@link #setForcedMaterial(com.jme3.material.Material) forced material} is - * set on this RenderManager, then it is used for rendering the geometry, - * otherwise, the {@link Geometry#getMaterial() geometry's material} is used. - * <p> - * If a {@link #setForcedTechnique(java.lang.String) forced technique} is - * set on this RenderManager, then it is selected automatically - * on the geometry's material and is used for rendering. Otherwise, one - * of the {@link MaterialDef#getDefaultTechniques() default techniques} is - * used. - * <p> - * If a {@link #setForcedRenderState(com.jme3.material.RenderState) forced - * render state} is set on this RenderManager, then it is used - * for rendering the material, and the material's own render state is ignored. - * Otherwise, the material's render state is used as intended. - *  - * @param g The geometry to render - *  - * @see Technique - * @see RenderState - * @see Material#selectTechnique(java.lang.String, com.jme3.renderer.RenderManager)  - * @see Material#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager)  - */ - public void renderGeometry(Geometry g) { - if (g.isIgnoreTransform()) { - setWorldMatrix(Matrix4f.IDENTITY); - } else { - setWorldMatrix(g.getWorldMatrix()); - } - - //if forcedTechnique we try to force it for render, - //if it does not exists in the mat def, we check for forcedMaterial and render the geom if not null - //else the geom is not rendered - if (forcedTechnique != null) { - if (g.getMaterial().getMaterialDef().getTechniqueDef(forcedTechnique) != null) { - tmpTech = g.getMaterial().getActiveTechnique() != null ? g.getMaterial().getActiveTechnique().getDef().getName() : "Default"; - g.getMaterial().selectTechnique(forcedTechnique, this); - // use geometry's material - g.getMaterial().render(g, this); - g.getMaterial().selectTechnique(tmpTech, this); - //Reverted this part from revision 6197 - //If forcedTechnique does not exists, and frocedMaterial is not set, the geom MUST NOT be rendered - } else if (forcedMaterial != null) { - // use forced material - forcedMaterial.render(g, this); - } - } else if (forcedMaterial != null) { - // use forced material - forcedMaterial.render(g, this); - } else { - g.getMaterial().render(g, this); - } - } - - /** - * Renders the given GeometryList. - * <p> - * For every geometry in the list, the  - * {@link #renderGeometry(com.jme3.scene.Geometry) } method is called. - *  - * @param gl The geometry list to render. - *  - * @see GeometryList - * @see #renderGeometry(com.jme3.scene.Geometry)  - */ - public void renderGeometryList(GeometryList gl) { - for (int i = 0; i < gl.size(); i++) { - renderGeometry(gl.get(i)); - } - } - - /** - * If a spatial is not inside the eye frustum, it - * is still rendered in the shadow frustum (shadow casting queue) - * through this recursive method. - */ - private void renderShadow(Spatial s, RenderQueue rq) { - if (s instanceof Node) { - Node n = (Node) s; - List<Spatial> children = n.getChildren(); - for (int i = 0; i < children.size(); i++) { - renderShadow(children.get(i), rq); - } - } else if (s instanceof Geometry) { - Geometry gm = (Geometry) s; - - RenderQueue.ShadowMode shadowMode = s.getShadowMode(); - if (shadowMode != RenderQueue.ShadowMode.Off && shadowMode != RenderQueue.ShadowMode.Receive) { - //forcing adding to shadow cast mode, culled objects doesn't have to be in the receiver queue - rq.addToShadowQueue(gm, RenderQueue.ShadowMode.Cast); - } - } - } - - /** - * Preloads a scene for rendering. - * <p> - * After invocation of this method, the underlying - * renderer would have uploaded any textures, shaders and meshes - * used by the given scene to the video driver.  - * Using this method is useful when wishing to avoid the initial pause - * when rendering a scene for the first time. Note that it is not  - * guaranteed that the underlying renderer will actually choose to upload - * the data to the GPU so some pause is still to be expected. - *  - * @param scene The scene to preload - */ - public void preloadScene(Spatial scene) { - if (scene instanceof Node) { - // recurse for all children - Node n = (Node) scene; - List<Spatial> children = n.getChildren(); - for (int i = 0; i < children.size(); i++) { - preloadScene(children.get(i)); - } - } else if (scene instanceof Geometry) { - // add to the render queue - Geometry gm = (Geometry) scene; - if (gm.getMaterial() == null) { - throw new IllegalStateException("No material is set for Geometry: " + gm.getName()); - } - - gm.getMaterial().preload(this); - Mesh mesh = gm.getMesh(); - if (mesh != null) { - for (Entry<VertexBuffer> entry : mesh.getBuffers()) { - VertexBuffer buf = entry.getValue(); - if (buf.getData() != null) { - renderer.updateBufferData(buf); - } - } - } - } - } - - /** - * Flattens the given scene graph into the ViewPort's RenderQueue, - * checking for culling as the call goes down the graph recursively. - * <p> - * First, the scene is checked for culling based on the <code>Spatial</code>s - * {@link Spatial#setCullHint(com.jme3.scene.Spatial.CullHint) cull hint}, - * if the camera frustum contains the scene, then this method is recursively - * called on its children. - * <p> - * When the scene's leaves or {@link Geometry geometries} are reached, - * they are each enqueued into the  - * {@link ViewPort#getQueue() ViewPort's render queue}. - * <p> - * In addition to enqueuing the visible geometries, this method - * also scenes which cast or receive shadows, by putting them into the - * RenderQueue's  - * {@link RenderQueue#addToShadowQueue(com.jme3.scene.Geometry, com.jme3.renderer.queue.RenderQueue.ShadowMode)  - * shadow queue}. Each Spatial which has its  - * {@link Spatial#setShadowMode(com.jme3.renderer.queue.RenderQueue.ShadowMode) shadow mode} - * set to not off, will be put into the appropriate shadow queue, note that - * this process does not check for frustum culling on any  - * {@link ShadowMode#Cast shadow casters}, as they don't have to be - * in the eye camera frustum to cast shadows on objects that are inside it. - *  - * @param scene The scene to flatten into the queue - * @param vp The ViewPort provides the {@link ViewPort#getCamera() camera} - * used for culling and the {@link ViewPort#getQueue() queue} used to  - * contain the flattened scene graph. - */ - public void renderScene(Spatial scene, ViewPort vp) { - if (scene.getParent() == null) { - vp.getCamera().setPlaneState(0); - } - // check culling first. - if (!scene.checkCulling(vp.getCamera())) { - // move on to shadow-only render - if ((scene.getShadowMode() != RenderQueue.ShadowMode.Off || scene instanceof Node) && scene.getCullHint()!=Spatial.CullHint.Always) { - renderShadow(scene, vp.getQueue()); - } - return; - } - - scene.runControlRender(this, vp); - if (scene instanceof Node) { - // recurse for all children - Node n = (Node) scene; - List<Spatial> children = n.getChildren(); - //saving cam state for culling - int camState = vp.getCamera().getPlaneState(); - for (int i = 0; i < children.size(); i++) { - //restoring cam state before proceeding children recusively - vp.getCamera().setPlaneState(camState); - renderScene(children.get(i), vp); - - } - } else if (scene instanceof Geometry) { - - // add to the render queue - Geometry gm = (Geometry) scene; - if (gm.getMaterial() == null) { - throw new IllegalStateException("No material is set for Geometry: " + gm.getName()); - } - - vp.getQueue().addToQueue(gm, scene.getQueueBucket()); - - // add to shadow queue if needed - RenderQueue.ShadowMode shadowMode = scene.getShadowMode(); - if (shadowMode != RenderQueue.ShadowMode.Off) { - vp.getQueue().addToShadowQueue(gm, shadowMode); - } - } - } - - /** - * Returns the camera currently used for rendering. - * <p> - * The camera can be set with {@link #setCamera(com.jme3.renderer.Camera, boolean) }. - *  - * @return the camera currently used for rendering. - */ - public Camera getCurrentCamera() { - return prevCam; - } - - /** - * The renderer implementation used for rendering operations. - *  - * @return The renderer implementation - *  - * @see #RenderManager(com.jme3.renderer.Renderer)  - * @see Renderer - */ - public Renderer getRenderer() { - return renderer; - } - - /** - * Flushes the ViewPort's {@link ViewPort#getQueue() render queue} - * by rendering each of its visible buckets. - * By default the queues will automatically be cleared after rendering, - * so there's no need to clear them manually. - *  - * @param vp The ViewPort of which the queue will be flushed - *  - * @see RenderQueue#renderQueue(com.jme3.renderer.queue.RenderQueue.Bucket, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera)  - * @see #renderGeometryList(com.jme3.renderer.queue.GeometryList)  - */ - public void flushQueue(ViewPort vp) { - renderViewPortQueues(vp, true); - } - - /** - * Clears the queue of the given ViewPort. - * Simply calls {@link RenderQueue#clear() } on the ViewPort's  - * {@link ViewPort#getQueue() render queue}. - *  - * @param vp The ViewPort of which the queue will be cleared. - *  - * @see RenderQueue#clear() - * @see ViewPort#getQueue() - */ - public void clearQueue(ViewPort vp) { - vp.getQueue().clear(); - } - - /** - * Render the given viewport queues. - * <p> - * Changes the {@link Renderer#setDepthRange(float, float) depth range} - * appropriately as expected by each queue and then calls  - * {@link RenderQueue#renderQueue(com.jme3.renderer.queue.RenderQueue.Bucket, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera, boolean) } - * on the queue. Makes sure to restore the depth range to [0, 1]  - * at the end of the call. - * Note that the {@link Bucket#Translucent translucent bucket} is NOT - * rendered by this method. Instead the user should call  - * {@link #renderTranslucentQueue(com.jme3.renderer.ViewPort) } - * after this call. - *  - * @param vp the viewport of which queue should be rendered - * @param flush If true, the queues will be cleared after - * rendering. - *  - * @see RenderQueue - * @see #renderTranslucentQueue(com.jme3.renderer.ViewPort)  - */ - public void renderViewPortQueues(ViewPort vp, boolean flush) { - RenderQueue rq = vp.getQueue(); - Camera cam = vp.getCamera(); - boolean depthRangeChanged = false; - - // render opaque objects with default depth range - // opaque objects are sorted front-to-back, reducing overdraw - rq.renderQueue(Bucket.Opaque, this, cam, flush); - - // render the sky, with depth range set to the farthest - if (!rq.isQueueEmpty(Bucket.Sky)) { - renderer.setDepthRange(1, 1); - rq.renderQueue(Bucket.Sky, this, cam, flush); - depthRangeChanged = true; - } - - - // transparent objects are last because they require blending with the - // rest of the scene's objects. Consequently, they are sorted - // back-to-front. - if (!rq.isQueueEmpty(Bucket.Transparent)) { - if (depthRangeChanged) { - renderer.setDepthRange(0, 1); - depthRangeChanged = false; - } - - rq.renderQueue(Bucket.Transparent, this, cam, flush); - } - - if (!rq.isQueueEmpty(Bucket.Gui)) { - renderer.setDepthRange(0, 0); - setCamera(cam, true); - rq.renderQueue(Bucket.Gui, this, cam, flush); - setCamera(cam, false); - depthRangeChanged = true; - } - - // restore range to default - if (depthRangeChanged) { - renderer.setDepthRange(0, 1); - } - } - - /** - * Renders the {@link Bucket#Translucent translucent queue} on the viewPort. - * <p> - * This call does nothing unless {@link #setHandleTranslucentBucket(boolean) } - * is set to true. This method clears the translucent queue after rendering - * it. - *  - * @param vp The viewport of which the translucent queue should be rendered. - *  - * @see #renderViewPortQueues(com.jme3.renderer.ViewPort, boolean)  - * @see #setHandleTranslucentBucket(boolean)  - */ - public void renderTranslucentQueue(ViewPort vp) { - RenderQueue rq = vp.getQueue(); - if (!rq.isQueueEmpty(Bucket.Translucent) && handleTranlucentBucket) { - rq.renderQueue(Bucket.Translucent, this, vp.getCamera(), true); - } - } - - private void setViewPort(Camera cam) { - // this will make sure to update viewport only if needed - if (cam != prevCam || cam.isViewportChanged()) { - viewX = (int) (cam.getViewPortLeft() * cam.getWidth()); - viewY = (int) (cam.getViewPortBottom() * cam.getHeight()); - viewWidth = (int) ((cam.getViewPortRight() - cam.getViewPortLeft()) * cam.getWidth()); - viewHeight = (int) ((cam.getViewPortTop() - cam.getViewPortBottom()) * cam.getHeight()); - renderer.setViewPort(viewX, viewY, viewWidth, viewHeight); - renderer.setClipRect(viewX, viewY, viewWidth, viewHeight); - cam.clearViewportChanged(); - prevCam = cam; - -// float translateX = viewWidth == viewX ? 0 : -(viewWidth + viewX) / (viewWidth - viewX); -// float translateY = viewHeight == viewY ? 0 : -(viewHeight + viewY) / (viewHeight - viewY); -// float scaleX = viewWidth == viewX ? 1f : 2f / (viewWidth - viewX); -// float scaleY = viewHeight == viewY ? 1f : 2f / (viewHeight - viewY); -//  -// orthoMatrix.loadIdentity(); -// orthoMatrix.setTranslation(translateX, translateY, 0); -// orthoMatrix.setScale(scaleX, scaleY, 0);  - - orthoMatrix.loadIdentity(); - orthoMatrix.setTranslation(-1f, -1f, 0f); - orthoMatrix.setScale(2f / cam.getWidth(), 2f / cam.getHeight(), 0f); - } - } - - private void setViewProjection(Camera cam, boolean ortho) { - if (shader) { - if (ortho) { - viewMatrix.set(Matrix4f.IDENTITY); - projMatrix.set(orthoMatrix); - viewProjMatrix.set(orthoMatrix); - } else { - viewMatrix.set(cam.getViewMatrix()); - projMatrix.set(cam.getProjectionMatrix()); - viewProjMatrix.set(cam.getViewProjectionMatrix()); - } - - camLoc.set(cam.getLocation()); - cam.getLeft(camLeft); - cam.getUp(camUp); - cam.getDirection(camDir); - - near = cam.getFrustumNear(); - far = cam.getFrustumFar(); - } else { - if (ortho) { - renderer.setViewProjectionMatrices(Matrix4f.IDENTITY, orthoMatrix); - } else { - renderer.setViewProjectionMatrices(cam.getViewMatrix(), - cam.getProjectionMatrix()); - } - - } - } - - /** - * Set the camera to use for rendering. - * <p> - * First, the camera's  - * {@link Camera#setViewPort(float, float, float, float) view port parameters} - * are applied. Then, the camera's {@link Camera#getViewMatrix() view} and  - * {@link Camera#getProjectionMatrix() projection} matrices are set - * on the renderer. If <code>ortho</code> is <code>true</code>, then - * instead of using the camera's view and projection matrices, an ortho - * matrix is computed and used instead of the view projection matrix.  - * The ortho matrix converts from the range (0 ~ Width, 0 ~ Height, -1 ~ +1) - * to the clip range (-1 ~ +1, -1 ~ +1, -1 ~ +1). - *  - * @param cam The camera to set - * @param ortho True if to use orthographic projection (for GUI rendering), - * false if to use the camera's view and projection matrices. - */ - public void setCamera(Camera cam, boolean ortho) { - setViewPort(cam); - setViewProjection(cam, ortho); - } - - /** - * Draws the viewport but without notifying {@link SceneProcessor scene - * processors} of any rendering events. - *  - * @param vp The ViewPort to render - *  - * @see #renderViewPort(com.jme3.renderer.ViewPort, float)  - */ - public void renderViewPortRaw(ViewPort vp) { - setCamera(vp.getCamera(), false); - List<Spatial> scenes = vp.getScenes(); - for (int i = scenes.size() - 1; i >= 0; i--) { - renderScene(scenes.get(i), vp); - } - flushQueue(vp); - } - - /** - * Renders the {@link ViewPort}. - * <p> - * If the ViewPort is {@link ViewPort#isEnabled() disabled}, this method - * returns immediately. Otherwise, the ViewPort is rendered by  - * the following process:<br> - * <ul> - * <li>All {@link SceneProcessor scene processors} that are attached - * to the ViewPort are {@link SceneProcessor#initialize(com.jme3.renderer.RenderManager, com.jme3.renderer.ViewPort) initialized}. - * </li> - * <li>The SceneProcessors' {@link SceneProcessor#preFrame(float) } method  - * is called.</li> - * <li>The ViewPort's {@link ViewPort#getOutputFrameBuffer() output framebuffer} - * is set on the Renderer</li> - * <li>The camera is set on the renderer, including its view port parameters. - * (see {@link #setCamera(com.jme3.renderer.Camera, boolean) })</li> - * <li>Any buffers that the ViewPort requests to be cleared are cleared - * and the {@link ViewPort#getBackgroundColor() background color} is set</li> - * <li>Every scene that is attached to the ViewPort is flattened into  - * the ViewPort's render queue  - * (see {@link #renderViewPortQueues(com.jme3.renderer.ViewPort, boolean) }) - * </li> - * <li>The SceneProcessors' {@link SceneProcessor#postQueue(com.jme3.renderer.queue.RenderQueue) } - * method is called.</li> - * <li>The render queue is sorted and then flushed, sending - * rendering commands to the underlying Renderer implementation.  - * (see {@link #flushQueue(com.jme3.renderer.ViewPort) })</li> - * <li>The SceneProcessors' {@link SceneProcessor#postFrame(com.jme3.texture.FrameBuffer) } - * method is called.</li> - * <li>The translucent queue of the ViewPort is sorted and then flushed - * (see {@link #renderTranslucentQueue(com.jme3.renderer.ViewPort) })</li> - * <li>If any objects remained in the render queue, they are removed - * from the queue. This is generally objects added to the  - * {@link RenderQueue#renderShadowQueue(com.jme3.renderer.queue.RenderQueue.ShadowMode, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera, boolean)  - * shadow queue} - * which were not rendered because of a missing shadow renderer.</li> - * </ul> - *  - * @param vp - * @param tpf  - */ - public void renderViewPort(ViewPort vp, float tpf) { - if (!vp.isEnabled()) { - return; - } - List<SceneProcessor> processors = vp.getProcessors(); - if (processors.isEmpty()) { - processors = null; - } - - if (processors != null) { - for (SceneProcessor proc : processors) { - if (!proc.isInitialized()) { - proc.initialize(this, vp); - } - proc.preFrame(tpf); - } - } - - renderer.setFrameBuffer(vp.getOutputFrameBuffer()); - setCamera(vp.getCamera(), false); - if (vp.isClearDepth() || vp.isClearColor() || vp.isClearStencil()) { - if (vp.isClearColor()) { - renderer.setBackgroundColor(vp.getBackgroundColor()); - } - renderer.clearBuffers(vp.isClearColor(), - vp.isClearDepth(), - vp.isClearStencil()); - } - - List<Spatial> scenes = vp.getScenes(); - for (int i = scenes.size() - 1; i >= 0; i--) { - renderScene(scenes.get(i), vp); - } - - if (processors != null) { - for (SceneProcessor proc : processors) { - proc.postQueue(vp.getQueue()); - } - } - - flushQueue(vp); - - if (processors != null) { - for (SceneProcessor proc : processors) { - proc.postFrame(vp.getOutputFrameBuffer()); - } - } - //renders the translucent objects queue after processors have been rendered - renderTranslucentQueue(vp); - // clear any remaining spatials that were not rendered. - clearQueue(vp); - } - - /** - * Called by the application to render any ViewPorts - * added to this RenderManager. - * <p> - * Renders any viewports that were added using the following methods: - * <ul> - * <li>{@link #createPreView(java.lang.String, com.jme3.renderer.Camera) }</li> - * <li>{@link #createMainView(java.lang.String, com.jme3.renderer.Camera) }</li> - * <li>{@link #createPostView(java.lang.String, com.jme3.renderer.Camera) }</li> - * </ul> - *  - * @param tpf Time per frame value - */ - public void render(float tpf, boolean mainFrameBufferActive) { - if (renderer instanceof NullRenderer) { - return; - } - - this.shader = renderer.getCaps().contains(Caps.GLSL100); - - for (int i = 0; i < preViewPorts.size(); i++) { - ViewPort vp = preViewPorts.get(i); - if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){ - renderViewPort(vp, tpf); - } - } - for (int i = 0; i < viewPorts.size(); i++) { - ViewPort vp = viewPorts.get(i); - if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){ - renderViewPort(vp, tpf); - } - } - for (int i = 0; i < postViewPorts.size(); i++) { - ViewPort vp = postViewPorts.get(i); - if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){ - renderViewPort(vp, tpf); - } - } - } -} +/*  + * Copyright (c) 2009-2012 jMonkeyEngine  + * All rights reserved.  + *  + * Redistribution and use in source and binary forms, with or without  + * modification, are permitted provided that the following conditions are  + * met:  + *  + * * Redistributions of source code must retain the above copyright  + * notice, this list of conditions and the following disclaimer.  + *  + * * Redistributions in binary form must reproduce the above copyright  + * notice, this list of conditions and the following disclaimer in the  + * documentation and/or other materials provided with the distribution.  + *  + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors  + * may be used to endorse or promote products derived from this software  + * without specific prior written permission.  + *  + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED  + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR  + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR  + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,  + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,  + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR  + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF  + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING  + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS  + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  + */  +package com.jme3.renderer;  +  +import com.jme3.material.Material;  +import com.jme3.material.MaterialDef;  +import com.jme3.material.RenderState;  +import com.jme3.material.Technique;  +import com.jme3.math.*;  +import com.jme3.post.SceneProcessor;  +import com.jme3.renderer.queue.GeometryList;  +import com.jme3.renderer.queue.RenderQueue;  +import com.jme3.renderer.queue.RenderQueue.Bucket;  +import com.jme3.renderer.queue.RenderQueue.ShadowMode;  +import com.jme3.scene.*;  +import com.jme3.shader.Uniform;  +import com.jme3.shader.UniformBinding;  +import com.jme3.shader.VarType;  +import com.jme3.system.NullRenderer;  +import com.jme3.system.Timer;  +import com.jme3.util.IntMap.Entry;  +import com.jme3.util.TempVars;  +import java.util.ArrayList;  +import java.util.Collections;  +import java.util.List;  +import java.util.logging.Logger;  +  +/**  + * <code>RenderManager</code> is a high-level rendering interface that is  + * above the Renderer implementation. RenderManager takes care  + * of rendering the scene graphs attached to each viewport and  + * handling SceneProcessors.  + *  + * @see SceneProcessor  + * @see ViewPort  + * @see Spatial  + */  +public class RenderManager {  +  + private static final Logger logger = Logger.getLogger(RenderManager.class.getName());  +  + private Renderer renderer;  + private Timer timer;  + private ArrayList<ViewPort> preViewPorts = new ArrayList<ViewPort>();  + private ArrayList<ViewPort> viewPorts = new ArrayList<ViewPort>();  + private ArrayList<ViewPort> postViewPorts = new ArrayList<ViewPort>();  + private Camera prevCam = null;  + private Material forcedMaterial = null;  + private String forcedTechnique = null;  + private RenderState forcedRenderState = null;  + private boolean shader;  + private int viewX, viewY, viewWidth, viewHeight;  + private float near, far;  + private Matrix4f orthoMatrix = new Matrix4f();  + private Matrix4f viewMatrix = new Matrix4f();  + private Matrix4f projMatrix = new Matrix4f();  + private Matrix4f viewProjMatrix = new Matrix4f();  + private Matrix4f worldMatrix = new Matrix4f();  + private Vector3f camUp = new Vector3f(),  + camLeft = new Vector3f(),  + camDir = new Vector3f(),  + camLoc = new Vector3f();  + //temp technique  + private String tmpTech;  + private boolean handleTranlucentBucket = true;  +  + /**  + * Create a high-level rendering interface over the  + * low-level rendering interface.  + * @param renderer  + */  + public RenderManager(Renderer renderer) {  + this.renderer = renderer;  + //this.shader = renderer.getCaps().contains(Caps.GLSL100);  + }  +  + /**  + * Returns the pre ViewPort with the given name.  + *  + * @param viewName The name of the pre ViewPort to look up  + * @return The ViewPort, or null if not found.  + *  + * @see #createPreView(java.lang.String, com.jme3.renderer.Camera)  + */  + public ViewPort getPreView(String viewName) {  + for (int i = 0; i < preViewPorts.size(); i++) {  + if (preViewPorts.get(i).getName().equals(viewName)) {  + return preViewPorts.get(i);  + }  + }  + return null;  + }  +  + /**  + * Removes the specified pre ViewPort.  + *  + * @param view The pre ViewPort to remove  + * @return True if the ViewPort was removed successfully.  + *  + * @see #createPreView(java.lang.String, com.jme3.renderer.Camera)  + */  + public boolean removePreView(ViewPort view) {  + return preViewPorts.remove(view);  + }  +  + /**  + * Returns the main ViewPort with the given name.  + *  + * @param viewName The name of the main ViewPort to look up  + * @return The ViewPort, or null if not found.  + *  + * @see #createMainView(java.lang.String, com.jme3.renderer.Camera)  + */  + public ViewPort getMainView(String viewName) {  + for (int i = 0; i < viewPorts.size(); i++) {  + if (viewPorts.get(i).getName().equals(viewName)) {  + return viewPorts.get(i);  + }  + }  + return null;  + }  +  + /**  + * Removes the main ViewPort with the specified name.  + *  + * @param viewName The main ViewPort name to remove  + * @return True if the ViewPort was removed successfully.  + *  + * @see #createMainView(java.lang.String, com.jme3.renderer.Camera)  + */  + public boolean removeMainView(String viewName) {  + for (int i = 0; i < viewPorts.size(); i++) {  + if (viewPorts.get(i).getName().equals(viewName)) {  + viewPorts.remove(i);  + return true;  + }  + }  + return false;  + }  +  + /**  + * Removes the specified main ViewPort.  + *  + * @param view The main ViewPort to remove  + * @return True if the ViewPort was removed successfully.  + *  + * @see #createMainView(java.lang.String, com.jme3.renderer.Camera)  + */  + public boolean removeMainView(ViewPort view) {  + return viewPorts.remove(view);  + }  +  + /**  + * Returns the post ViewPort with the given name.  + *  + * @param viewName The name of the post ViewPort to look up  + * @return The ViewPort, or null if not found.  + *  + * @see #createPostView(java.lang.String, com.jme3.renderer.Camera)  + */  + public ViewPort getPostView(String viewName) {  + for (int i = 0; i < postViewPorts.size(); i++) {  + if (postViewPorts.get(i).getName().equals(viewName)) {  + return postViewPorts.get(i);  + }  + }  + return null;  + }  +  + /**  + * Removes the post ViewPort with the specified name.  + *  + * @param viewName The post ViewPort name to remove  + * @return True if the ViewPort was removed successfully.  + *  + * @see #createPostView(java.lang.String, com.jme3.renderer.Camera)  + */  + public boolean removePostView(String viewName) {  + for (int i = 0; i < postViewPorts.size(); i++) {  + if (postViewPorts.get(i).getName().equals(viewName)) {  + postViewPorts.remove(i);  +  + return true;  + }  + }  + return false;  + }  +  + /**  + * Removes the specified post ViewPort.  + *  + * @param view The post ViewPort to remove  + * @return True if the ViewPort was removed successfully.  + *  + * @see #createPostView(java.lang.String, com.jme3.renderer.Camera)  + */  + public boolean removePostView(ViewPort view) {  + return postViewPorts.remove(view);  + }  +  + /**  + * Returns a read-only list of all pre ViewPorts  + * @return a read-only list of all pre ViewPorts  + * @see #createPreView(java.lang.String, com.jme3.renderer.Camera)  + */  + public List<ViewPort> getPreViews() {  + return Collections.unmodifiableList(preViewPorts);  + }  +  + /**  + * Returns a read-only list of all main ViewPorts  + * @return a read-only list of all main ViewPorts  + * @see #createMainView(java.lang.String, com.jme3.renderer.Camera)  + */  + public List<ViewPort> getMainViews() {  + return Collections.unmodifiableList(viewPorts);  + }  +  + /**  + * Returns a read-only list of all post ViewPorts  + * @return a read-only list of all post ViewPorts  + * @see #createPostView(java.lang.String, com.jme3.renderer.Camera)  + */  + public List<ViewPort> getPostViews() {  + return Collections.unmodifiableList(postViewPorts);  + }  +  + /**  + * Creates a new pre ViewPort, to display the given camera's content.  + * <p>  + * The view will be processed before the main and post viewports.  + */  + public ViewPort createPreView(String viewName, Camera cam) {  + ViewPort vp = new ViewPort(viewName, cam);  + preViewPorts.add(vp);  + return vp;  + }  +  + /**  + * Creates a new main ViewPort, to display the given camera's content.  + * <p>  + * The view will be processed before the post viewports but after  + * the pre viewports.  + */  + public ViewPort createMainView(String viewName, Camera cam) {  + ViewPort vp = new ViewPort(viewName, cam);  + viewPorts.add(vp);  + return vp;  + }  +  + /**  + * Creates a new post ViewPort, to display the given camera's content.  + * <p>  + * The view will be processed after the pre and main viewports.  + */  + public ViewPort createPostView(String viewName, Camera cam) {  + ViewPort vp = new ViewPort(viewName, cam);  + postViewPorts.add(vp);  + return vp;  + }  +  + private void notifyReshape(ViewPort vp, int w, int h) {  + List<SceneProcessor> processors = vp.getProcessors();  + for (SceneProcessor proc : processors) {  + if (!proc.isInitialized()) {  + proc.initialize(this, vp);  + } else {  + proc.reshape(vp, w, h);  + }  + }  + }  +  + /**  + * Internal use only.  + * Updates the resolution of all on-screen cameras to match  + * the given width and height.  + */  + public void notifyReshape(int w, int h) {  + for (ViewPort vp : preViewPorts) {  + if (vp.getOutputFrameBuffer() == null) {  + Camera cam = vp.getCamera();  + cam.resize(w, h, true);  + }  + notifyReshape(vp, w, h);  + }  + for (ViewPort vp : viewPorts) {  + if (vp.getOutputFrameBuffer() == null) {  + Camera cam = vp.getCamera();  + cam.resize(w, h, true);  + }  + notifyReshape(vp, w, h);  + }  + for (ViewPort vp : postViewPorts) {  + if (vp.getOutputFrameBuffer() == null) {  + Camera cam = vp.getCamera();  + cam.resize(w, h, true);  + }  + notifyReshape(vp, w, h);  + }  + }  +  + /**  + * Internal use only.  + * Updates the given list of uniforms with {@link UniformBinding uniform bindings}  + * based on the current world state.  + */  + public void updateUniformBindings(List<Uniform> params) {  + // assums worldMatrix is properly set.  + TempVars vars = TempVars.get();  +  + Matrix4f tempMat4 = vars.tempMat4;  + Matrix3f tempMat3 = vars.tempMat3;  + Vector2f tempVec2 = vars.vect2d;  + Quaternion tempVec4 = vars.quat1;  +  + for (int i = 0; i < params.size(); i++) {  + Uniform u = params.get(i);  + switch (u.getBinding()) {  + case WorldMatrix:  + u.setValue(VarType.Matrix4, worldMatrix);  + break;  + case ViewMatrix:  + u.setValue(VarType.Matrix4, viewMatrix);  + break;  + case ProjectionMatrix:  + u.setValue(VarType.Matrix4, projMatrix);  + break;  + case ViewProjectionMatrix:  + u.setValue(VarType.Matrix4, viewProjMatrix);  + break;  + case WorldViewMatrix:  + tempMat4.set(viewMatrix);  + tempMat4.multLocal(worldMatrix);  + u.setValue(VarType.Matrix4, tempMat4);  + break;  + case NormalMatrix:  + tempMat4.set(viewMatrix);  + tempMat4.multLocal(worldMatrix);  + tempMat4.toRotationMatrix(tempMat3);  + tempMat3.invertLocal();  + tempMat3.transposeLocal();  + u.setValue(VarType.Matrix3, tempMat3);  + break;  + case WorldViewProjectionMatrix:  + tempMat4.set(viewProjMatrix);  + tempMat4.multLocal(worldMatrix);  + u.setValue(VarType.Matrix4, tempMat4);  + break;  + case WorldMatrixInverse:  + tempMat4.set(worldMatrix);  + tempMat4.invertLocal();  + u.setValue(VarType.Matrix4, tempMat4);  + break;  + case WorldMatrixInverseTranspose:  + worldMatrix.toRotationMatrix(tempMat3);  + tempMat3.invertLocal().transposeLocal();  + u.setValue(VarType.Matrix3, tempMat3);  + break;  + case ViewMatrixInverse:  + tempMat4.set(viewMatrix);  + tempMat4.invertLocal();  + u.setValue(VarType.Matrix4, tempMat4);  + break;  + case ProjectionMatrixInverse:  + tempMat4.set(projMatrix);  + tempMat4.invertLocal();  + u.setValue(VarType.Matrix4, tempMat4);  + break;  + case ViewProjectionMatrixInverse:  + tempMat4.set(viewProjMatrix);  + tempMat4.invertLocal();  + u.setValue(VarType.Matrix4, tempMat4);  + break;  + case WorldViewMatrixInverse:  + tempMat4.set(viewMatrix);  + tempMat4.multLocal(worldMatrix);  + tempMat4.invertLocal();  + u.setValue(VarType.Matrix4, tempMat4);  + break;  + case NormalMatrixInverse:  + tempMat4.set(viewMatrix);  + tempMat4.multLocal(worldMatrix);  + tempMat4.toRotationMatrix(tempMat3);  + tempMat3.invertLocal();  + tempMat3.transposeLocal();  + tempMat3.invertLocal();  + u.setValue(VarType.Matrix3, tempMat3);  + break;  + case WorldViewProjectionMatrixInverse:  + tempMat4.set(viewProjMatrix);  + tempMat4.multLocal(worldMatrix);  + tempMat4.invertLocal();  + u.setValue(VarType.Matrix4, tempMat4);  + break;  + case ViewPort:  + tempVec4.set(viewX, viewY, viewWidth, viewHeight);  + u.setValue(VarType.Vector4, tempVec4);  + break;  + case Resolution:  + tempVec2.set(viewWidth, viewHeight);  + u.setValue(VarType.Vector2, tempVec2);  + break;  + case ResolutionInverse:  + tempVec2.set(1f / viewWidth, 1f / viewHeight);  + u.setValue(VarType.Vector2, tempVec2);  + break;  + case Aspect:  + float aspect = ((float) viewWidth) / viewHeight;  + u.setValue(VarType.Float, aspect);  + break;  + case FrustumNearFar:  + tempVec2.set(near, far);  + u.setValue(VarType.Vector2, tempVec2);  + break;  + case CameraPosition:  + u.setValue(VarType.Vector3, camLoc);  + break;  + case CameraDirection:  + u.setValue(VarType.Vector3, camDir);  + break;  + case CameraLeft:  + u.setValue(VarType.Vector3, camLeft);  + break;  + case CameraUp:  + u.setValue(VarType.Vector3, camUp);  + break;  + case Time:  + u.setValue(VarType.Float, timer.getTimeInSeconds());  + break;  + case Tpf:  + u.setValue(VarType.Float, timer.getTimePerFrame());  + break;  + case FrameRate:  + u.setValue(VarType.Float, timer.getFrameRate());  + break;  + }  + }  +  + vars.release();  + }  +  + /**  + * Set the material to use to render all future objects.  + * This overrides the material set on the geometry and renders  + * with the provided material instead.  + * Use null to clear the material and return renderer to normal  + * functionality.  + * @param mat The forced material to set, or null to return to normal  + */  + public void setForcedMaterial(Material mat) {  + forcedMaterial = mat;  + }  +  + /**  + * Returns the forced render state previously set with  + * {@link #setForcedRenderState(com.jme3.material.RenderState) }.  + * @return the forced render state  + */  + public RenderState getForcedRenderState() {  + return forcedRenderState;  + }  +  + /**  + * Set the render state to use for all future objects.  + * This overrides the render state set on the material and instead  + * forces this render state to be applied for all future materials  + * rendered. Set to null to return to normal functionality.  + *  + * @param forcedRenderState The forced render state to set, or null  + * to return to normal  + */  + public void setForcedRenderState(RenderState forcedRenderState) {  + this.forcedRenderState = forcedRenderState;  + }  +  + /**  + * Set the timer that should be used to query the time based  + * {@link UniformBinding}s for material world parameters.  + *  + * @param timer The timer to query time world parameters  + */  + public void setTimer(Timer timer) {  + this.timer = timer;  + }  +  + /**  + * Returns the forced technique name set.  + *  + * @return the forced technique name set.  + *  + * @see #setForcedTechnique(java.lang.String)  + */  + public String getForcedTechnique() {  + return forcedTechnique;  + }  +  + /**  + * Sets the forced technique to use when rendering geometries.  + * <p>  + * If the specified technique name is available on the geometry's  + * material, then it is used, otherwise, the  + * {@link #setForcedMaterial(com.jme3.material.Material) forced material} is used.  + * If a forced material is not set and the forced technique name cannot  + * be found on the material, the geometry will <em>not</em> be rendered.  + *  + * @param forcedTechnique The forced technique name to use, set to null  + * to return to normal functionality.  + *  + * @see #renderGeometry(com.jme3.scene.Geometry)  + */  + public void setForcedTechnique(String forcedTechnique) {  + this.forcedTechnique = forcedTechnique;  + }  +  + /**  + * Enable or disable alpha-to-coverage.  + * <p>  + * When alpha to coverage is enabled and the renderer implementation  + * supports it, then alpha blending will be replaced with alpha dissolve  + * if multi-sampling is also set on the renderer.  + * This feature allows avoiding of alpha blending artifacts due to  + * lack of triangle-level back-to-front sorting.  + *  + * @param value True to enable alpha-to-coverage, false otherwise.  + */  + public void setAlphaToCoverage(boolean value) {  + renderer.setAlphaToCoverage(value);  + }  +  + /**  + * True if the translucent bucket should automatically be rendered  + * by the RenderManager.  + *  + * @return Whether or not the translucent bucket is rendered.  + *  + * @see #setHandleTranslucentBucket(boolean)  + */  + public boolean isHandleTranslucentBucket() {  + return handleTranlucentBucket;  + }  +  + /**  + * Enable or disable rendering of the  + * {@link Bucket#Translucent translucent bucket}  + * by the RenderManager. The default is enabled.  + *  + * @param handleTranslucentBucket Whether or not the translucent bucket should  + * be rendered.  + */  + public void setHandleTranslucentBucket(boolean handleTranslucentBucket) {  + this.handleTranlucentBucket = handleTranslucentBucket;  + }  +  + /**  + * Internal use only. Sets the world matrix to use for future  + * rendering. This has no effect unless objects are rendered manually  + * using {@link Material#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager) }.  + * Using {@link #renderGeometry(com.jme3.scene.Geometry) } will  + * override this value.  + *  + * @param mat The world matrix to set  + */  + public void setWorldMatrix(Matrix4f mat) {  + if (shader) {  + worldMatrix.set(mat);  + } else {  + renderer.setWorldMatrix(mat);  + }  + }  +  + /**  + * Renders the given geometry.  + * <p>  + * First the proper world matrix is set, if  + * the geometry's {@link Geometry#setIgnoreTransform(boolean) ignore transform}  + * feature is enabled, the identity world matrix is used, otherwise, the  + * geometry's {@link Geometry#getWorldMatrix() world transform matrix} is used.  + * <p>  + * Once the world matrix is applied, the proper material is chosen for rendering.  + * If a {@link #setForcedMaterial(com.jme3.material.Material) forced material} is  + * set on this RenderManager, then it is used for rendering the geometry,  + * otherwise, the {@link Geometry#getMaterial() geometry's material} is used.  + * <p>  + * If a {@link #setForcedTechnique(java.lang.String) forced technique} is  + * set on this RenderManager, then it is selected automatically  + * on the geometry's material and is used for rendering. Otherwise, one  + * of the {@link MaterialDef#getDefaultTechniques() default techniques} is  + * used.  + * <p>  + * If a {@link #setForcedRenderState(com.jme3.material.RenderState) forced  + * render state} is set on this RenderManager, then it is used  + * for rendering the material, and the material's own render state is ignored.  + * Otherwise, the material's render state is used as intended.  + *  + * @param g The geometry to render  + *  + * @see Technique  + * @see RenderState  + * @see Material#selectTechnique(java.lang.String, com.jme3.renderer.RenderManager)  + * @see Material#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager)  + */  + public void renderGeometry(Geometry g) {  + if (g.isIgnoreTransform()) {  + setWorldMatrix(Matrix4f.IDENTITY);  + } else {  + setWorldMatrix(g.getWorldMatrix());  + }  +  + //if forcedTechnique we try to force it for render,  + //if it does not exists in the mat def, we check for forcedMaterial and render the geom if not null  + //else the geom is not rendered  + if (forcedTechnique != null) {  + if (g.getMaterial().getMaterialDef().getTechniqueDef(forcedTechnique) != null) {  + tmpTech = g.getMaterial().getActiveTechnique() != null ? g.getMaterial().getActiveTechnique().getDef().getName() : "Default";  + g.getMaterial().selectTechnique(forcedTechnique, this);  + // use geometry's material  + g.getMaterial().render(g, this);  + g.getMaterial().selectTechnique(tmpTech, this);  + //Reverted this part from revision 6197  + //If forcedTechnique does not exists, and frocedMaterial is not set, the geom MUST NOT be rendered  + } else if (forcedMaterial != null) {  + // use forced material  + forcedMaterial.render(g, this);  + }  + } else if (forcedMaterial != null) {  + // use forced material  + forcedMaterial.render(g, this);  + } else {  + g.getMaterial().render(g, this);  + }  + }  +  + /**  + * Renders the given GeometryList.  + * <p>  + * For every geometry in the list, the  + * {@link #renderGeometry(com.jme3.scene.Geometry) } method is called.  + *  + * @param gl The geometry list to render.  + *  + * @see GeometryList  + * @see #renderGeometry(com.jme3.scene.Geometry)  + */  + public void renderGeometryList(GeometryList gl) {  + for (int i = 0; i < gl.size(); i++) {  + renderGeometry(gl.get(i));  + }  + }  +  + /**  + * If a spatial is not inside the eye frustum, it  + * is still rendered in the shadow frustum (shadow casting queue)  + * through this recursive method.  + */  + private void renderShadow(Spatial s, RenderQueue rq) {  + if (s instanceof Node) {  + Node n = (Node) s;  + List<Spatial> children = n.getChildren();  + for (int i = 0; i < children.size(); i++) {  + renderShadow(children.get(i), rq);  + }  + } else if (s instanceof Geometry) {  + Geometry gm = (Geometry) s;  +  + RenderQueue.ShadowMode shadowMode = s.getShadowMode();  + if (shadowMode != RenderQueue.ShadowMode.Off && shadowMode != RenderQueue.ShadowMode.Receive) {  + //forcing adding to shadow cast mode, culled objects doesn't have to be in the receiver queue  + rq.addToShadowQueue(gm, RenderQueue.ShadowMode.Cast);  + }  + }  + }  +  + /**  + * Preloads a scene for rendering.  + * <p>  + * After invocation of this method, the underlying  + * renderer would have uploaded any textures, shaders and meshes  + * used by the given scene to the video driver.  + * Using this method is useful when wishing to avoid the initial pause  + * when rendering a scene for the first time. Note that it is not  + * guaranteed that the underlying renderer will actually choose to upload  + * the data to the GPU so some pause is still to be expected.  + *  + * @param scene The scene to preload  + */  + public void preloadScene(Spatial scene) {  + if (scene instanceof Node) {  + // recurse for all children  + Node n = (Node) scene;  + List<Spatial> children = n.getChildren();  + for (int i = 0; i < children.size(); i++) {  + preloadScene(children.get(i));  + }  + } else if (scene instanceof Geometry) {  + // add to the render queue  + Geometry gm = (Geometry) scene;  + if (gm.getMaterial() == null) {  + throw new IllegalStateException("No material is set for Geometry: " + gm.getName());  + }  +  + gm.getMaterial().preload(this);  + Mesh mesh = gm.getMesh();  + if (mesh != null) {  + for (VertexBuffer vb : mesh.getBufferList().getArray()) {  + if (vb.getData() != null) {  + renderer.updateBufferData(vb);  + }  + }  + }  + }  + }  +  + /**  + * Flattens the given scene graph into the ViewPort's RenderQueue,  + * checking for culling as the call goes down the graph recursively.  + * <p>  + * First, the scene is checked for culling based on the <code>Spatial</code>s  + * {@link Spatial#setCullHint(com.jme3.scene.Spatial.CullHint) cull hint},  + * if the camera frustum contains the scene, then this method is recursively  + * called on its children.  + * <p>  + * When the scene's leaves or {@link Geometry geometries} are reached,  + * they are each enqueued into the  + * {@link ViewPort#getQueue() ViewPort's render queue}.  + * <p>  + * In addition to enqueuing the visible geometries, this method  + * also scenes which cast or receive shadows, by putting them into the  + * RenderQueue's  + * {@link RenderQueue#addToShadowQueue(com.jme3.scene.Geometry, com.jme3.renderer.queue.RenderQueue.ShadowMode)  + * shadow queue}. Each Spatial which has its  + * {@link Spatial#setShadowMode(com.jme3.renderer.queue.RenderQueue.ShadowMode) shadow mode}  + * set to not off, will be put into the appropriate shadow queue, note that  + * this process does not check for frustum culling on any  + * {@link ShadowMode#Cast shadow casters}, as they don't have to be  + * in the eye camera frustum to cast shadows on objects that are inside it.  + *  + * @param scene The scene to flatten into the queue  + * @param vp The ViewPort provides the {@link ViewPort#getCamera() camera}  + * used for culling and the {@link ViewPort#getQueue() queue} used to  + * contain the flattened scene graph.  + */  + public void renderScene(Spatial scene, ViewPort vp) {  + if (scene.getParent() == null) {  + vp.getCamera().setPlaneState(0);  + }  + // check culling first.  + if (!scene.checkCulling(vp.getCamera())) {  + // move on to shadow-only render  + if ((scene.getShadowMode() != RenderQueue.ShadowMode.Off || scene instanceof Node) && scene.getCullHint()!=Spatial.CullHint.Always) {  + renderShadow(scene, vp.getQueue());  + }  + return;  + }  +  + scene.runControlRender(this, vp);  + if (scene instanceof Node) {  + // recurse for all children  + Node n = (Node) scene;  + List<Spatial> children = n.getChildren();  + //saving cam state for culling  + int camState = vp.getCamera().getPlaneState();  + for (int i = 0; i < children.size(); i++) {  + //restoring cam state before proceeding children recusively  + vp.getCamera().setPlaneState(camState);  + renderScene(children.get(i), vp);  +  + }  + } else if (scene instanceof Geometry) {  +  + // add to the render queue  + Geometry gm = (Geometry) scene;  + if (gm.getMaterial() == null) {  + throw new IllegalStateException("No material is set for Geometry: " + gm.getName());  + }  +  + vp.getQueue().addToQueue(gm, scene.getQueueBucket());  +  + // add to shadow queue if needed  + RenderQueue.ShadowMode shadowMode = scene.getShadowMode();  + if (shadowMode != RenderQueue.ShadowMode.Off) {  + vp.getQueue().addToShadowQueue(gm, shadowMode);  + }  + }  + }  +  + /**  + * Returns the camera currently used for rendering.  + * <p>  + * The camera can be set with {@link #setCamera(com.jme3.renderer.Camera, boolean) }.  + *  + * @return the camera currently used for rendering.  + */  + public Camera getCurrentCamera() {  + return prevCam;  + }  +  + /**  + * The renderer implementation used for rendering operations.  + *  + * @return The renderer implementation  + *  + * @see #RenderManager(com.jme3.renderer.Renderer)  + * @see Renderer  + */  + public Renderer getRenderer() {  + return renderer;  + }  +  + /**  + * Flushes the ViewPort's {@link ViewPort#getQueue() render queue}  + * by rendering each of its visible buckets.  + * By default the queues will automatically be cleared after rendering,  + * so there's no need to clear them manually.  + *  + * @param vp The ViewPort of which the queue will be flushed  + *  + * @see RenderQueue#renderQueue(com.jme3.renderer.queue.RenderQueue.Bucket, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera)  + * @see #renderGeometryList(com.jme3.renderer.queue.GeometryList)  + */  + public void flushQueue(ViewPort vp) {  + renderViewPortQueues(vp, true);  + }  +  + /**  + * Clears the queue of the given ViewPort.  + * Simply calls {@link RenderQueue#clear() } on the ViewPort's  + * {@link ViewPort#getQueue() render queue}.  + *  + * @param vp The ViewPort of which the queue will be cleared.  + *  + * @see RenderQueue#clear()  + * @see ViewPort#getQueue()  + */  + public void clearQueue(ViewPort vp) {  + vp.getQueue().clear();  + }  +  + /**  + * Render the given viewport queues.  + * <p>  + * Changes the {@link Renderer#setDepthRange(float, float) depth range}  + * appropriately as expected by each queue and then calls  + * {@link RenderQueue#renderQueue(com.jme3.renderer.queue.RenderQueue.Bucket, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera, boolean) }  + * on the queue. Makes sure to restore the depth range to [0, 1]  + * at the end of the call.  + * Note that the {@link Bucket#Translucent translucent bucket} is NOT  + * rendered by this method. Instead the user should call  + * {@link #renderTranslucentQueue(com.jme3.renderer.ViewPort) }  + * after this call.  + *  + * @param vp the viewport of which queue should be rendered  + * @param flush If true, the queues will be cleared after  + * rendering.  + *  + * @see RenderQueue  + * @see #renderTranslucentQueue(com.jme3.renderer.ViewPort)  + */  + public void renderViewPortQueues(ViewPort vp, boolean flush) {  + RenderQueue rq = vp.getQueue();  + Camera cam = vp.getCamera();  + boolean depthRangeChanged = false;  +  + // render opaque objects with default depth range  + // opaque objects are sorted front-to-back, reducing overdraw  + rq.renderQueue(Bucket.Opaque, this, cam, flush);  +  + // render the sky, with depth range set to the farthest  + if (!rq.isQueueEmpty(Bucket.Sky)) {  + renderer.setDepthRange(1, 1);  + rq.renderQueue(Bucket.Sky, this, cam, flush);  + depthRangeChanged = true;  + }  +  +  + // transparent objects are last because they require blending with the  + // rest of the scene's objects. Consequently, they are sorted  + // back-to-front.  + if (!rq.isQueueEmpty(Bucket.Transparent)) {  + if (depthRangeChanged) {  + renderer.setDepthRange(0, 1);  + depthRangeChanged = false;  + }  +  + rq.renderQueue(Bucket.Transparent, this, cam, flush);  + }  +  + if (!rq.isQueueEmpty(Bucket.Gui)) {  + renderer.setDepthRange(0, 0);  + setCamera(cam, true);  + rq.renderQueue(Bucket.Gui, this, cam, flush);  + setCamera(cam, false);  + depthRangeChanged = true;  + }  +  + // restore range to default  + if (depthRangeChanged) {  + renderer.setDepthRange(0, 1);  + }  + }  +  + /**  + * Renders the {@link Bucket#Translucent translucent queue} on the viewPort.  + * <p>  + * This call does nothing unless {@link #setHandleTranslucentBucket(boolean) }  + * is set to true. This method clears the translucent queue after rendering  + * it.  + *  + * @param vp The viewport of which the translucent queue should be rendered.  + *  + * @see #renderViewPortQueues(com.jme3.renderer.ViewPort, boolean)  + * @see #setHandleTranslucentBucket(boolean)  + */  + public void renderTranslucentQueue(ViewPort vp) {  + RenderQueue rq = vp.getQueue();  + if (!rq.isQueueEmpty(Bucket.Translucent) && handleTranlucentBucket) {  + rq.renderQueue(Bucket.Translucent, this, vp.getCamera(), true);  + }  + }  +  + private void setViewPort(Camera cam) {  + // this will make sure to update viewport only if needed  + if (cam != prevCam || cam.isViewportChanged()) {  + viewX = (int) (cam.getViewPortLeft() * cam.getWidth());  + viewY = (int) (cam.getViewPortBottom() * cam.getHeight());  + viewWidth = (int) ((cam.getViewPortRight() - cam.getViewPortLeft()) * cam.getWidth());  + viewHeight = (int) ((cam.getViewPortTop() - cam.getViewPortBottom()) * cam.getHeight());  + renderer.setViewPort(viewX, viewY, viewWidth, viewHeight);  + renderer.setClipRect(viewX, viewY, viewWidth, viewHeight);  + cam.clearViewportChanged();  + prevCam = cam;  +  +// float translateX = viewWidth == viewX ? 0 : -(viewWidth + viewX) / (viewWidth - viewX);  +// float translateY = viewHeight == viewY ? 0 : -(viewHeight + viewY) / (viewHeight - viewY);  +// float scaleX = viewWidth == viewX ? 1f : 2f / (viewWidth - viewX);  +// float scaleY = viewHeight == viewY ? 1f : 2f / (viewHeight - viewY);  +//  +// orthoMatrix.loadIdentity();  +// orthoMatrix.setTranslation(translateX, translateY, 0);  +// orthoMatrix.setScale(scaleX, scaleY, 0);  +  + orthoMatrix.loadIdentity();  + orthoMatrix.setTranslation(-1f, -1f, 0f);  + orthoMatrix.setScale(2f / cam.getWidth(), 2f / cam.getHeight(), 0f);  + }  + }  +  + private void setViewProjection(Camera cam, boolean ortho) {  + if (shader) {  + if (ortho) {  + viewMatrix.set(Matrix4f.IDENTITY);  + projMatrix.set(orthoMatrix);  + viewProjMatrix.set(orthoMatrix);  + } else {  + viewMatrix.set(cam.getViewMatrix());  + projMatrix.set(cam.getProjectionMatrix());  + viewProjMatrix.set(cam.getViewProjectionMatrix());  + }  +  + camLoc.set(cam.getLocation());  + cam.getLeft(camLeft);  + cam.getUp(camUp);  + cam.getDirection(camDir);  +  + near = cam.getFrustumNear();  + far = cam.getFrustumFar();  + } else {  + if (ortho) {  + renderer.setViewProjectionMatrices(Matrix4f.IDENTITY, orthoMatrix);  + } else {  + renderer.setViewProjectionMatrices(cam.getViewMatrix(),  + cam.getProjectionMatrix());  + }  +  + }  + }  +  + /**  + * Set the camera to use for rendering.  + * <p>  + * First, the camera's  + * {@link Camera#setViewPort(float, float, float, float) view port parameters}  + * are applied. Then, the camera's {@link Camera#getViewMatrix() view} and  + * {@link Camera#getProjectionMatrix() projection} matrices are set  + * on the renderer. If <code>ortho</code> is <code>true</code>, then  + * instead of using the camera's view and projection matrices, an ortho  + * matrix is computed and used instead of the view projection matrix.  + * The ortho matrix converts from the range (0 ~ Width, 0 ~ Height, -1 ~ +1)  + * to the clip range (-1 ~ +1, -1 ~ +1, -1 ~ +1).  + *  + * @param cam The camera to set  + * @param ortho True if to use orthographic projection (for GUI rendering),  + * false if to use the camera's view and projection matrices.  + */  + public void setCamera(Camera cam, boolean ortho) {  + setViewPort(cam);  + setViewProjection(cam, ortho);  + }  +  + /**  + * Draws the viewport but without notifying {@link SceneProcessor scene  + * processors} of any rendering events.  + *  + * @param vp The ViewPort to render  + *  + * @see #renderViewPort(com.jme3.renderer.ViewPort, float)  + */  + public void renderViewPortRaw(ViewPort vp) {  + setCamera(vp.getCamera(), false);  + List<Spatial> scenes = vp.getScenes();  + for (int i = scenes.size() - 1; i >= 0; i--) {  + renderScene(scenes.get(i), vp);  + }  + flushQueue(vp);  + }  +  + /**  + * Renders the {@link ViewPort}.  + * <p>  + * If the ViewPort is {@link ViewPort#isEnabled() disabled}, this method  + * returns immediately. Otherwise, the ViewPort is rendered by  + * the following process:<br>  + * <ul>  + * <li>All {@link SceneProcessor scene processors} that are attached  + * to the ViewPort are {@link SceneProcessor#initialize(com.jme3.renderer.RenderManager, com.jme3.renderer.ViewPort) initialized}.  + * </li>  + * <li>The SceneProcessors' {@link SceneProcessor#preFrame(float) } method  + * is called.</li>  + * <li>The ViewPort's {@link ViewPort#getOutputFrameBuffer() output framebuffer}  + * is set on the Renderer</li>  + * <li>The camera is set on the renderer, including its view port parameters.  + * (see {@link #setCamera(com.jme3.renderer.Camera, boolean) })</li>  + * <li>Any buffers that the ViewPort requests to be cleared are cleared  + * and the {@link ViewPort#getBackgroundColor() background color} is set</li>  + * <li>Every scene that is attached to the ViewPort is flattened into  + * the ViewPort's render queue  + * (see {@link #renderViewPortQueues(com.jme3.renderer.ViewPort, boolean) })  + * </li>  + * <li>The SceneProcessors' {@link SceneProcessor#postQueue(com.jme3.renderer.queue.RenderQueue) }  + * method is called.</li>  + * <li>The render queue is sorted and then flushed, sending  + * rendering commands to the underlying Renderer implementation.  + * (see {@link #flushQueue(com.jme3.renderer.ViewPort) })</li>  + * <li>The SceneProcessors' {@link SceneProcessor#postFrame(com.jme3.texture.FrameBuffer) }  + * method is called.</li>  + * <li>The translucent queue of the ViewPort is sorted and then flushed  + * (see {@link #renderTranslucentQueue(com.jme3.renderer.ViewPort) })</li>  + * <li>If any objects remained in the render queue, they are removed  + * from the queue. This is generally objects added to the  + * {@link RenderQueue#renderShadowQueue(com.jme3.renderer.queue.RenderQueue.ShadowMode, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera, boolean)  + * shadow queue}  + * which were not rendered because of a missing shadow renderer.</li>  + * </ul>  + *  + * @param vp  + * @param tpf  + */  + public void renderViewPort(ViewPort vp, float tpf) {  + if (!vp.isEnabled()) {  + return;  + }  + List<SceneProcessor> processors = vp.getProcessors();  + if (processors.isEmpty()) {  + processors = null;  + }  +  + if (processors != null) {  + for (SceneProcessor proc : processors) {  + if (!proc.isInitialized()) {  + proc.initialize(this, vp);  + }  + proc.preFrame(tpf);  + }  + }  +  + renderer.setFrameBuffer(vp.getOutputFrameBuffer());  + setCamera(vp.getCamera(), false);  + if (vp.isClearDepth() || vp.isClearColor() || vp.isClearStencil()) {  + if (vp.isClearColor()) {  + renderer.setBackgroundColor(vp.getBackgroundColor());  + }  + renderer.clearBuffers(vp.isClearColor(),  + vp.isClearDepth(),  + vp.isClearStencil());  + }  +  + List<Spatial> scenes = vp.getScenes();  + for (int i = scenes.size() - 1; i >= 0; i--) {  + renderScene(scenes.get(i), vp);  + }  +  + if (processors != null) {  + for (SceneProcessor proc : processors) {  + proc.postQueue(vp.getQueue());  + }  + }  +  + flushQueue(vp);  +  + if (processors != null) {  + for (SceneProcessor proc : processors) {  + proc.postFrame(vp.getOutputFrameBuffer());  + }  + }  + //renders the translucent objects queue after processors have been rendered  + renderTranslucentQueue(vp);  + // clear any remaining spatials that were not rendered.  + clearQueue(vp);  + }  +  + /**  + * Called by the application to render any ViewPorts  + * added to this RenderManager.  + * <p>  + * Renders any viewports that were added using the following methods:  + * <ul>  + * <li>{@link #createPreView(java.lang.String, com.jme3.renderer.Camera) }</li>  + * <li>{@link #createMainView(java.lang.String, com.jme3.renderer.Camera) }</li>  + * <li>{@link #createPostView(java.lang.String, com.jme3.renderer.Camera) }</li>  + * </ul>  + *  + * @param tpf Time per frame value  + */  + public void render(float tpf, boolean mainFrameBufferActive) {  + if (renderer instanceof NullRenderer) {  + return;  + }  +  + this.shader = renderer.getCaps().contains(Caps.GLSL100);  +  + for (int i = 0; i < preViewPorts.size(); i++) {  + ViewPort vp = preViewPorts.get(i);  + if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){  + renderViewPort(vp, tpf);  + }  + }  + for (int i = 0; i < viewPorts.size(); i++) {  + ViewPort vp = viewPorts.get(i);  + if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){  + renderViewPort(vp, tpf);  + }  + }  + for (int i = 0; i < postViewPorts.size(); i++) {  + ViewPort vp = postViewPorts.get(i);  + if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){  + renderViewPort(vp, tpf);  + }  + }  + }  +}  
diff --git a/engine/src/core/com/jme3/scene/BatchNode.java b/engine/src/core/com/jme3/scene/BatchNode.java index bc1b2cb..303de7a 100644 --- a/engine/src/core/com/jme3/scene/BatchNode.java +++ b/engine/src/core/com/jme3/scene/BatchNode.java 
@@ -46,6 +46,7 @@  import java.util.HashMap;  import java.util.List;  import java.util.Map; +import java.util.Set;  import java.util.logging.Level;  import java.util.logging.Logger;   @@ -190,10 +191,10 @@  }  batches.clear();  } -  - for (Material material : matMap.keySet()) { + for (Map.Entry<Material, List<Geometry>> entry : matMap.entrySet()) {  Mesh m = new Mesh(); - List<Geometry> list = matMap.get(material); + Material material = entry.getKey(); + List<Geometry> list = entry.getValue();  nbGeoms += list.size();  if (!needsFullRebatch) {  list.add(batches.get(material).geometry); @@ -408,9 +409,9 @@  throw new UnsupportedOperationException();  }   - for (Entry<VertexBuffer> entry : geom.getMesh().getBuffers()) { - compsForBuf[entry.getKey()] = entry.getValue().getNumComponents(); - formatForBuf[entry.getKey()] = entry.getValue().getFormat(); + for (VertexBuffer vb : geom.getMesh().getBufferList().getArray()) { + compsForBuf[vb.getBufferType().ordinal()] = vb.getNumComponents(); + formatForBuf[vb.getBufferType().ordinal()] = vb.getFormat();  }    if (mode != null && mode != listMode) { 
diff --git a/engine/src/core/com/jme3/scene/Mesh.java b/engine/src/core/com/jme3/scene/Mesh.java index 6c587f2..036e525 100644 --- a/engine/src/core/com/jme3/scene/Mesh.java +++ b/engine/src/core/com/jme3/scene/Mesh.java 
@@ -43,7 +43,6 @@  import com.jme3.math.Triangle;  import com.jme3.math.Vector2f;  import com.jme3.math.Vector3f; -import com.jme3.scene.VertexBuffer;  import com.jme3.scene.VertexBuffer.Format;  import com.jme3.scene.VertexBuffer.Type;  import com.jme3.scene.VertexBuffer.Usage; @@ -55,8 +54,6 @@  import java.io.IOException;  import java.nio.*;  import java.util.ArrayList; -import java.util.HashSet; -import java.util.Set;    /**  * <code>Mesh</code> is used to store rendering data. @@ -237,9 +234,9 @@    clone.buffers = new IntMap<VertexBuffer>();  clone.buffersList = new SafeArrayList<VertexBuffer>(VertexBuffer.class); - for (Entry<VertexBuffer> ent : buffers){ - VertexBuffer bufClone = ent.getValue().clone(); - clone.buffers.put(ent.getKey(), bufClone); + for (VertexBuffer vb : buffersList.getArray()){ + VertexBuffer bufClone = vb.clone(); + clone.buffers.put(vb.getBufferType().ordinal(), bufClone);  clone.buffersList.add(bufClone);  }   @@ -540,8 +537,8 @@  * for all {@link VertexBuffer vertex buffers} on this Mesh.  */  public void setStatic() { - for (Entry<VertexBuffer> entry : buffers){ - entry.getValue().setUsage(Usage.Static); + for (VertexBuffer vb : buffersList.getArray()){ + vb.setUsage(Usage.Static);  }  }   @@ -551,8 +548,8 @@  * for all {@link VertexBuffer vertex buffers} on this Mesh.  */  public void setDynamic() { - for (Entry<VertexBuffer> entry : buffers){ - entry.getValue().setUsage(Usage.Dynamic); + for (VertexBuffer vb : buffersList.getArray()){ + vb.setUsage(Usage.Dynamic);  }  }   @@ -562,8 +559,8 @@  * for all {@link VertexBuffer vertex buffers} on this Mesh.  */  public void setStreamed(){ - for (Entry<VertexBuffer> entry : buffers){ - entry.getValue().setUsage(Usage.Stream); + for (VertexBuffer vb : buffersList.getArray()){ + vb.setUsage(Usage.Stream);  }  }   @@ -572,11 +569,11 @@  * Some GPUs may prefer the data in this format, however it is a good idea  * to <em>avoid</em> using this method as it disables some engine features.  */ + @Deprecated  public void setInterleaved(){  ArrayList<VertexBuffer> vbs = new ArrayList<VertexBuffer>(); - for (Entry<VertexBuffer> entry : buffers){ - vbs.add(entry.getValue()); - } + vbs.addAll(buffersList); +   // ArrayList<VertexBuffer> vbs = new ArrayList<VertexBuffer>(buffers.values());  // index buffer not included when interleaving  vbs.remove(getBuffer(Type.Index)); @@ -860,6 +857,65 @@  }    /** + * Sets the {@link VertexBuffer} on the mesh. + * This will update the vertex/triangle counts if needed. + *  + * @param vb The buffer to set + * @throws IllegalArgumentException If the buffer type is already set + */ + public void setBuffer(VertexBuffer vb){ + if (buffers.containsKey(vb.getBufferType().ordinal())) + throw new IllegalArgumentException("Buffer type already set: "+vb.getBufferType()); + + buffers.put(vb.getBufferType().ordinal(), vb); + buffersList.add(vb); + updateCounts(); + } +  + /** + * Unsets the {@link VertexBuffer} set on this mesh + * with the given type. Does nothing if the vertex buffer type is not set  + * initially. + *  + * @param type The buffer type to remove + */ + public void clearBuffer(VertexBuffer.Type type){ + VertexBuffer vb = buffers.remove(type.ordinal()); + if (vb != null){ + buffersList.remove(vb); + updateCounts(); + } + } +  + /** + * Creates a {@link VertexBuffer} for the mesh or modifies + * the existing one per the parameters given. + *  + * @param type The type of the buffer + * @param components Number of components + * @param format Data format + * @param buf The buffer data + *  + * @throws UnsupportedOperationException If the buffer already set is  + * incompatible with the parameters given. + */ + public void setBuffer(Type type, int components, Format format, Buffer buf){ + VertexBuffer vb = buffers.get(type.ordinal()); + if (vb == null){ + vb = new VertexBuffer(type); + vb.setupData(Usage.Dynamic, components, format, buf); + setBuffer(vb); + }else{ + if (vb.getNumComponents() != components || vb.getFormat() != format){ + throw new UnsupportedOperationException("The buffer already set " + + "is incompatible with the given parameters"); + } + vb.updateData(buf); + updateCounts(); + } + } +  + /**  * Set a floating point {@link VertexBuffer} on the mesh.   *   * @param type The type of {@link VertexBuffer},  @@ -871,21 +927,7 @@  * @param buf The floating point data to contain  */  public void setBuffer(Type type, int components, FloatBuffer buf) { -// VertexBuffer vb = buffers.get(type); - VertexBuffer vb = buffers.get(type.ordinal()); - if (vb == null){ - if (buf == null) - return; - - vb = new VertexBuffer(type); - vb.setupData(Usage.Dynamic, components, Format.Float, buf); -// buffers.put(type, vb); - buffers.put(type.ordinal(), vb); - buffersList.add(vb); - }else{ - vb.setupData(Usage.Dynamic, components, Format.Float, buf); - } - updateCounts(); + setBuffer(type, components, Format.Float, buf);  }    public void setBuffer(Type type, int components, float[] buf){ @@ -893,14 +935,7 @@  }    public void setBuffer(Type type, int components, IntBuffer buf) { - VertexBuffer vb = buffers.get(type.ordinal()); - if (vb == null){ - vb = new VertexBuffer(type); - vb.setupData(Usage.Dynamic, components, Format.UnsignedInt, buf); - buffers.put(type.ordinal(), vb); - buffersList.add(vb); - updateCounts(); - } + setBuffer(type, components, Format.UnsignedInt, buf);  }    public void setBuffer(Type type, int components, int[] buf){ @@ -908,14 +943,7 @@  }    public void setBuffer(Type type, int components, ShortBuffer buf) { - VertexBuffer vb = buffers.get(type.ordinal()); - if (vb == null){ - vb = new VertexBuffer(type); - vb.setupData(Usage.Dynamic, components, Format.UnsignedShort, buf); - buffers.put(type.ordinal(), vb); - buffersList.add(vb); - updateCounts(); - } + setBuffer(type, components, Format.UnsignedShort, buf);  }    public void setBuffer(Type type, int components, byte[] buf){ @@ -923,38 +951,7 @@  }    public void setBuffer(Type type, int components, ByteBuffer buf) { - VertexBuffer vb = buffers.get(type.ordinal()); - if (vb == null){ - vb = new VertexBuffer(type); - vb.setupData(Usage.Dynamic, components, Format.UnsignedByte, buf); - buffers.put(type.ordinal(), vb); - buffersList.add(vb); - updateCounts(); - } - } - - public void setBuffer(VertexBuffer vb){ - if (buffers.containsKey(vb.getBufferType().ordinal())) - throw new IllegalArgumentException("Buffer type already set: "+vb.getBufferType()); - - buffers.put(vb.getBufferType().ordinal(), vb); - buffersList.add(vb); - updateCounts(); - } - - /** - * Clears or unsets the {@link VertexBuffer} set on this mesh - * with the given type. - * Does nothing if the vertex buffer type is not set initially - *  - * @param type The type to remove - */ - public void clearBuffer(VertexBuffer.Type type){ - VertexBuffer vb = buffers.remove(type.ordinal()); - if (vb != null){ - buffersList.remove(vb); - updateCounts(); - } + setBuffer(type, components, Format.UnsignedByte, buf);  }    public void setBuffer(Type type, int components, short[] buf){ 
diff --git a/engine/src/core/com/jme3/shader/UniformBinding.java b/engine/src/core/com/jme3/shader/UniformBinding.java index bdca35b..6bd4ca9 100644 --- a/engine/src/core/com/jme3/shader/UniformBinding.java +++ b/engine/src/core/com/jme3/shader/UniformBinding.java 
@@ -1,162 +1,176 @@ -/* - * Copyright (c) 2009-2010 jMonkeyEngine - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of 'jMonkeyEngine' nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.jme3.shader; - -public enum UniformBinding { - - /** - * The world matrix. Converts Model space to World space. - * Type: mat4 - */ - WorldMatrix, - - /** - * The view matrix. Converts World space to View space. - * Type: mat4 - */ - ViewMatrix, - - /** - * The projection matrix. Converts View space to Clip/Projection space. - * Type: mat4 - */ - ProjectionMatrix, - - /** - * The world view matrix. Converts Model space to View space. - * Type: mat4 - */ - WorldViewMatrix, - - /** - * The normal matrix. The inverse transpose of the worldview matrix. - * Converts normals from model space to view space. - * Type: mat3 - */ - NormalMatrix, - - /** - * The world view projection matrix. Converts Model space to Clip/Projection - * space. - * Type: mat4 - */ - WorldViewProjectionMatrix, - - /** - * The view projection matrix. Converts Model space to Clip/Projection - * space. - * Type: mat4 - */ - ViewProjectionMatrix, - - - WorldMatrixInverse, - ViewMatrixInverse, - ProjectionMatrixInverse, - ViewProjectionMatrixInverse, - WorldViewMatrixInverse, - NormalMatrixInverse, - WorldViewProjectionMatrixInverse, - - /** - * Contains the four viewport parameters in this order: - * X = Left, - * Y = Top, - * Z = Right, - * W = Bottom. - * Type: vec4 - */ - ViewPort, - - /** - * The near and far values for the camera frustum. - * X = Near - * Y = Far. - * Type: vec2 - */ - FrustumNearFar, -  - /** - * The width and height of the camera. - * Type: vec2 - */ - Resolution, - - /** - * Aspect ratio of the resolution currently set. Width/Height. - * Type: float - */ - Aspect, - - /** - * Camera position in world space. - * Type: vec3 - */ - CameraPosition, - - /** - * Direction of the camera. - * Type: vec3 - */ - CameraDirection, - - /** - * Left vector of the camera. - * Type: vec3 - */ - CameraLeft, - - /** - * Up vector of the camera. - * Type: vec3 - */ - CameraUp, - - /** - * Time in seconds since the application was started. - * Type: float - */ - Time, - - /** - * Time in seconds that the last frame took. - * Type: float - */ - Tpf, - - /** - * Frames per second. - * Type: float - */ - FrameRate, -} +/*  + * Copyright (c) 2009-2010 jMonkeyEngine  + * All rights reserved.  + *  + * Redistribution and use in source and binary forms, with or without  + * modification, are permitted provided that the following conditions are  + * met:  + *  + * * Redistributions of source code must retain the above copyright  + * notice, this list of conditions and the following disclaimer.  + *  + * * Redistributions in binary form must reproduce the above copyright  + * notice, this list of conditions and the following disclaimer in the  + * documentation and/or other materials provided with the distribution.  + *  + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors  + * may be used to endorse or promote products derived from this software  + * without specific prior written permission.  + *  + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED  + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR  + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR  + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,  + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,  + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR  + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF  + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING  + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS  + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  + */  +  +package com.jme3.shader;  +  +public enum UniformBinding {  +  + /**  + * The world matrix. Converts Model space to World space.  + * Type: mat4  + */  + WorldMatrix,  +  + /**  + * The view matrix. Converts World space to View space.  + * Type: mat4  + */  + ViewMatrix,  +  + /**  + * The projection matrix. Converts View space to Clip/Projection space.  + * Type: mat4  + */  + ProjectionMatrix,  +  + /**  + * The world view matrix. Converts Model space to View space.  + * Type: mat4  + */  + WorldViewMatrix,  +  + /**  + * The normal matrix. The inverse transpose of the worldview matrix.  + * Converts normals from model space to view space.  + * Type: mat3  + */  + NormalMatrix,  +  + /**  + * The world view projection matrix. Converts Model space to Clip/Projection  + * space.  + * Type: mat4  + */  + WorldViewProjectionMatrix,  +  + /**  + * The view projection matrix. Converts World space to Clip/Projection  + * space.  + * Type: mat4  + */  + ViewProjectionMatrix,  +  + /**  + * The world matrix inverse transpose. Converts a normals from Model space  + * to world space.  + * Type: mat3  + */  + WorldMatrixInverseTranspose,  +  +  +  + WorldMatrixInverse,  + ViewMatrixInverse,  + ProjectionMatrixInverse,  + ViewProjectionMatrixInverse,  + WorldViewMatrixInverse,  + NormalMatrixInverse,  + WorldViewProjectionMatrixInverse,  +  + /**  + * Contains the four viewport parameters in this order:  + * X = Left,  + * Y = Top,  + * Z = Right,  + * W = Bottom.  + * Type: vec4  + */  + ViewPort,  +  + /**  + * The near and far values for the camera frustum.  + * X = Near  + * Y = Far.  + * Type: vec2  + */  + FrustumNearFar,  +  + /**  + * The width and height of the camera.  + * Type: vec2  + */  + Resolution,  +  + /**  + * The inverse of the resolution, 1/width and 1/height.  + * Type: vec2  + */  + ResolutionInverse,  +  + /**  + * Aspect ratio of the resolution currently set. Width/Height.  + * Type: float  + */  + Aspect,  +  + /**  + * Camera position in world space.  + * Type: vec3  + */  + CameraPosition,  +  + /**  + * Direction of the camera.  + * Type: vec3  + */  + CameraDirection,  +  + /**  + * Left vector of the camera.  + * Type: vec3  + */  + CameraLeft,  +  + /**  + * Up vector of the camera.  + * Type: vec3  + */  + CameraUp,  +  + /**  + * Time in seconds since the application was started.  + * Type: float  + */  + Time,  +  + /**  + * Time in seconds that the last frame took.  + * Type: float  + */  + Tpf,  +  + /**  + * Frames per second.  + * Type: float  + */  + FrameRate,  +}  
diff --git a/engine/src/core/com/jme3/system/JmeSystem.java b/engine/src/core/com/jme3/system/JmeSystem.java index 1a13a42..2e2d79a 100644 --- a/engine/src/core/com/jme3/system/JmeSystem.java +++ b/engine/src/core/com/jme3/system/JmeSystem.java 
@@ -1,136 +1,147 @@ -/* - * Copyright (c) 2009-2010 jMonkeyEngine - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of 'jMonkeyEngine' nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.jme3.system; - -import com.jme3.asset.AssetManager; -import com.jme3.audio.AudioRenderer; -import java.io.File; -import java.io.InputStream; -import java.net.URL; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class JmeSystem { - - private static JmeSystemDelegate systemDelegate; - - public static void setSystemDelegate(JmeSystemDelegate systemDelegate) { - JmeSystem.systemDelegate = systemDelegate; - } -  - public static synchronized File getStorageFolder() { - checkDelegate(); - return systemDelegate.getStorageFolder(); - } - - public static String getFullName() { - checkDelegate(); - return systemDelegate.getFullName(); - } - - public static InputStream getResourceAsStream(String name) { - checkDelegate(); - return systemDelegate.getResourceAsStream(name); - } - - public static URL getResource(String name) { - checkDelegate(); - return systemDelegate.getResource(name); - } - - public static boolean trackDirectMemory() { - checkDelegate(); - return systemDelegate.trackDirectMemory(); - } - - public static void setLowPermissions(boolean lowPerm) { - checkDelegate(); - systemDelegate.setLowPermissions(lowPerm); - } - - public static boolean isLowPermissions() { - checkDelegate(); - return systemDelegate.isLowPermissions(); - } - - public static AssetManager newAssetManager(URL configFile) { - checkDelegate(); - return systemDelegate.newAssetManager(configFile); - } - - public static AssetManager newAssetManager() { - checkDelegate(); - return systemDelegate.newAssetManager(); - } - - public static boolean showSettingsDialog(AppSettings sourceSettings, final boolean loadFromRegistry) { - checkDelegate(); - return systemDelegate.showSettingsDialog(sourceSettings, loadFromRegistry); - } - - public static Platform getPlatform() { - checkDelegate(); - return systemDelegate.getPlatform(); - } - - public static JmeContext newContext(AppSettings settings, JmeContext.Type contextType) { - checkDelegate(); - return systemDelegate.newContext(settings, contextType); - } - - public static AudioRenderer newAudioRenderer(AppSettings settings) { - checkDelegate(); - return systemDelegate.newAudioRenderer(settings); - } - - public static void initialize(AppSettings settings) { - checkDelegate(); - systemDelegate.initialize(settings); - } - - @SuppressWarnings("unchecked") - private static void checkDelegate() { - if (systemDelegate == null) { - Class<JmeSystemDelegate> systemDelegateClass; - try { - systemDelegateClass = (Class<JmeSystemDelegate>) Class.forName("com.jme3.system.JmeDesktopSystem"); - systemDelegate = systemDelegateClass.newInstance(); - } catch (InstantiationException ex) { - Logger.getLogger(JmeSystem.class.getName()).log(Level.SEVERE, "No JmeSystemDelegate specified, cannot instantiate default JmeDesktopSystem:\n{0}", ex); - } catch (IllegalAccessException ex) { - Logger.getLogger(JmeSystem.class.getName()).log(Level.SEVERE, "No JmeSystemDelegate specified, cannot instantiate default JmeDesktopSystem:\n{0}", ex); - } catch (ClassNotFoundException ex) { - Logger.getLogger(JmeSystem.class.getName()).log(Level.SEVERE, "No JmeSystemDelegate specified, cannot instantiate default JmeDesktopSystem:\n{0}", ex); - } - } - } -} +/*  + * Copyright (c) 2009-2010 jMonkeyEngine  + * All rights reserved.  + *  + * Redistribution and use in source and binary forms, with or without  + * modification, are permitted provided that the following conditions are  + * met:  + *  + * * Redistributions of source code must retain the above copyright  + * notice, this list of conditions and the following disclaimer.  + *  + * * Redistributions in binary form must reproduce the above copyright  + * notice, this list of conditions and the following disclaimer in the  + * documentation and/or other materials provided with the distribution.  + *  + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors  + * may be used to endorse or promote products derived from this software  + * without specific prior written permission.  + *  + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED  + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR  + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR  + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,  + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,  + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR  + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF  + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING  + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS  + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  + */  +package com.jme3.system;  +  +import com.jme3.asset.AssetManager;  +import com.jme3.audio.AudioRenderer;  +import com.jme3.input.SoftTextDialogInput;  +import java.io.File;  +import java.io.InputStream;  +import java.net.URL;  +import java.util.logging.Level;  +import java.util.logging.Logger;  +  +public class JmeSystem {  +  + private static JmeSystemDelegate systemDelegate;  +  + public static void setSystemDelegate(JmeSystemDelegate systemDelegate) {  + JmeSystem.systemDelegate = systemDelegate;  + }  +  + public static synchronized File getStorageFolder() {  + checkDelegate();  + return systemDelegate.getStorageFolder();  + }  +  + public static String getFullName() {  + checkDelegate();  + return systemDelegate.getFullName();  + }  +  + public static InputStream getResourceAsStream(String name) {  + checkDelegate();  + return systemDelegate.getResourceAsStream(name);  + }  +  + public static URL getResource(String name) {  + checkDelegate();  + return systemDelegate.getResource(name);  + }  +  + public static boolean trackDirectMemory() {  + checkDelegate();  + return systemDelegate.trackDirectMemory();  + }  +  + public static void setLowPermissions(boolean lowPerm) {  + checkDelegate();  + systemDelegate.setLowPermissions(lowPerm);  + }  +  + public static boolean isLowPermissions() {  + checkDelegate();  + return systemDelegate.isLowPermissions();  + }  +  + public static void setSoftTextDialogInput(SoftTextDialogInput input) {  + checkDelegate();  + systemDelegate.setSoftTextDialogInput(input);  + }  +  + public static SoftTextDialogInput getSoftTextDialogInput() {  + checkDelegate();  + return systemDelegate.getSoftTextDialogInput();  + }  +  + public static AssetManager newAssetManager(URL configFile) {  + checkDelegate();  + return systemDelegate.newAssetManager(configFile);  + }  +  + public static AssetManager newAssetManager() {  + checkDelegate();  + return systemDelegate.newAssetManager();  + }  +  + public static boolean showSettingsDialog(AppSettings sourceSettings, final boolean loadFromRegistry) {  + checkDelegate();  + return systemDelegate.showSettingsDialog(sourceSettings, loadFromRegistry);  + }  +  + public static Platform getPlatform() {  + checkDelegate();  + return systemDelegate.getPlatform();  + }  +  + public static JmeContext newContext(AppSettings settings, JmeContext.Type contextType) {  + checkDelegate();  + return systemDelegate.newContext(settings, contextType);  + }  +  + public static AudioRenderer newAudioRenderer(AppSettings settings) {  + checkDelegate();  + return systemDelegate.newAudioRenderer(settings);  + }  +  + public static void initialize(AppSettings settings) {  + checkDelegate();  + systemDelegate.initialize(settings);  + }  +  + @SuppressWarnings("unchecked")  + private static void checkDelegate() {  + if (systemDelegate == null) {  + Class<JmeSystemDelegate> systemDelegateClass;  + try {  + systemDelegateClass = (Class<JmeSystemDelegate>) Class.forName("com.jme3.system.JmeDesktopSystem");  + systemDelegate = systemDelegateClass.newInstance();  + } catch (InstantiationException ex) {  + Logger.getLogger(JmeSystem.class.getName()).log(Level.SEVERE, "No JmeSystemDelegate specified, cannot instantiate default JmeDesktopSystem:\n{0}", ex);  + } catch (IllegalAccessException ex) {  + Logger.getLogger(JmeSystem.class.getName()).log(Level.SEVERE, "No JmeSystemDelegate specified, cannot instantiate default JmeDesktopSystem:\n{0}", ex);  + } catch (ClassNotFoundException ex) {  + Logger.getLogger(JmeSystem.class.getName()).log(Level.SEVERE, "No JmeSystemDelegate specified, cannot instantiate default JmeDesktopSystem:\n{0}", ex);  + }  + }  + }  +}  
diff --git a/engine/src/core/com/jme3/system/JmeSystemDelegate.java b/engine/src/core/com/jme3/system/JmeSystemDelegate.java index 60265ae..e0f6fa5 100644 --- a/engine/src/core/com/jme3/system/JmeSystemDelegate.java +++ b/engine/src/core/com/jme3/system/JmeSystemDelegate.java 
@@ -1,140 +1,149 @@ -/* - * Copyright (c) 2009-2010 jMonkeyEngine - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of 'jMonkeyEngine' nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.jme3.system; - -import com.jme3.asset.AssetManager; -import com.jme3.audio.AudioRenderer; -import java.io.File; -import java.io.InputStream; -import java.net.URL; -import java.util.logging.Logger; - -/** - * - * @author Kirill Vainer, normenhansen - */ -public abstract class JmeSystemDelegate { - - protected final Logger logger = Logger.getLogger(JmeSystem.class.getName()); - protected boolean initialized = false; - protected boolean lowPermissions = false; - protected File storageFolder = null; - - public synchronized File getStorageFolder() { - if (lowPermissions) { - throw new UnsupportedOperationException("File system access restricted"); - } - if (storageFolder == null) { - // Initialize storage folder - storageFolder = new File(System.getProperty("user.home"), ".jme3"); - if (!storageFolder.exists()) { - storageFolder.mkdir(); - } - } - return storageFolder; - } -  - public String getFullName() { - return JmeVersion.FULL_NAME; - } - - public InputStream getResourceAsStream(String name) { - return this.getClass().getResourceAsStream(name); - } - - public URL getResource(String name) { - return this.getClass().getResource(name); - } - - public boolean trackDirectMemory() { - return false; - } - - public void setLowPermissions(boolean lowPerm) { - lowPermissions = lowPerm; - } - - public boolean isLowPermissions() { - return lowPermissions; - } - - public abstract AssetManager newAssetManager(URL configFile); - - public abstract AssetManager newAssetManager(); - - public abstract boolean showSettingsDialog(AppSettings sourceSettings, boolean loadFromRegistry); - - private boolean is64Bit(String arch) { - if (arch.equals("x86")) { - return false; - } else if (arch.equals("amd64")) { - return true; - } else if (arch.equals("x86_64")) { - return true; - } else if (arch.equals("ppc") || arch.equals("PowerPC")) { - return false; - } else if (arch.equals("ppc64")) { - return true; - } else if (arch.equals("i386") || arch.equals("i686")) { - return false; - } else if (arch.equals("universal")) { - return false; - } else { - throw new UnsupportedOperationException("Unsupported architecture: " + arch); - } - } - - public Platform getPlatform() { - String os = System.getProperty("os.name").toLowerCase(); - String arch = System.getProperty("os.arch").toLowerCase(); - boolean is64 = is64Bit(arch); - if (os.contains("windows")) { - return is64 ? Platform.Windows64 : Platform.Windows32; - } else if (os.contains("linux") || os.contains("freebsd") || os.contains("sunos")) { - return is64 ? Platform.Linux64 : Platform.Linux32; - } else if (os.contains("mac os x") || os.contains("darwin")) { - if (arch.startsWith("ppc")) { - return is64 ? Platform.MacOSX_PPC64 : Platform.MacOSX_PPC32; - } else { - return is64 ? Platform.MacOSX64 : Platform.MacOSX32; - } - } else { - throw new UnsupportedOperationException("The specified platform: " + os + " is not supported."); - } - } - - public abstract JmeContext newContext(AppSettings settings, JmeContext.Type contextType); - - public abstract AudioRenderer newAudioRenderer(AppSettings settings); - - public abstract void initialize(AppSettings settings); -} +/*  + * Copyright (c) 2009-2010 jMonkeyEngine  + * All rights reserved.  + *  + * Redistribution and use in source and binary forms, with or without  + * modification, are permitted provided that the following conditions are  + * met:  + *  + * * Redistributions of source code must retain the above copyright  + * notice, this list of conditions and the following disclaimer.  + *  + * * Redistributions in binary form must reproduce the above copyright  + * notice, this list of conditions and the following disclaimer in the  + * documentation and/or other materials provided with the distribution.  + *  + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors  + * may be used to endorse or promote products derived from this software  + * without specific prior written permission.  + *  + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED  + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR  + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR  + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,  + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,  + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR  + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF  + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING  + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS  + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  + */  +package com.jme3.system;  +  +import com.jme3.asset.AssetManager;  +import com.jme3.audio.AudioRenderer;  +import com.jme3.input.SoftTextDialogInput;  +import java.io.File;  +import java.io.InputStream;  +import java.net.URL;  +import java.util.logging.Logger;  +  +/**  + *  + * @author Kirill Vainer, normenhansen  + */  +public abstract class JmeSystemDelegate {  +  + protected final Logger logger = Logger.getLogger(JmeSystem.class.getName());  + protected boolean initialized = false;  + protected boolean lowPermissions = false;  + protected File storageFolder = null;  + protected SoftTextDialogInput softTextDialogInput = null;  +  + public synchronized File getStorageFolder() {  + if (lowPermissions) {  + throw new UnsupportedOperationException("File system access restricted");  + }  + if (storageFolder == null) {  + // Initialize storage folder  + storageFolder = new File(System.getProperty("user.home"), ".jme3");  + if (!storageFolder.exists()) {  + storageFolder.mkdir();  + }  + }  + return storageFolder;  + }  +  + public String getFullName() {  + return JmeVersion.FULL_NAME;  + }  +  + public InputStream getResourceAsStream(String name) {  + return this.getClass().getResourceAsStream(name);  + }  +  + public URL getResource(String name) {  + return this.getClass().getResource(name);  + }  +  + public boolean trackDirectMemory() {  + return false;  + }  +  + public void setLowPermissions(boolean lowPerm) {  + lowPermissions = lowPerm;  + }  +  + public boolean isLowPermissions() {  + return lowPermissions;  + }  +  + public void setSoftTextDialogInput(SoftTextDialogInput input) {  + softTextDialogInput = input;  + }  + public SoftTextDialogInput getSoftTextDialogInput() {  + return softTextDialogInput;  + }  +  + public abstract AssetManager newAssetManager(URL configFile);  +  + public abstract AssetManager newAssetManager();  +  + public abstract boolean showSettingsDialog(AppSettings sourceSettings, boolean loadFromRegistry);  +  + private boolean is64Bit(String arch) {  + if (arch.equals("x86")) {  + return false;  + } else if (arch.equals("amd64")) {  + return true;  + } else if (arch.equals("x86_64")) {  + return true;  + } else if (arch.equals("ppc") || arch.equals("PowerPC")) {  + return false;  + } else if (arch.equals("ppc64")) {  + return true;  + } else if (arch.equals("i386") || arch.equals("i686")) {  + return false;  + } else if (arch.equals("universal")) {  + return false;  + } else {  + throw new UnsupportedOperationException("Unsupported architecture: " + arch);  + }  + }  +  + public Platform getPlatform() {  + String os = System.getProperty("os.name").toLowerCase();  + String arch = System.getProperty("os.arch").toLowerCase();  + boolean is64 = is64Bit(arch);  + if (os.contains("windows")) {  + return is64 ? Platform.Windows64 : Platform.Windows32;  + } else if (os.contains("linux") || os.contains("freebsd") || os.contains("sunos")) {  + return is64 ? Platform.Linux64 : Platform.Linux32;  + } else if (os.contains("mac os x") || os.contains("darwin")) {  + if (arch.startsWith("ppc")) {  + return is64 ? Platform.MacOSX_PPC64 : Platform.MacOSX_PPC32;  + } else {  + return is64 ? Platform.MacOSX64 : Platform.MacOSX32;  + }  + } else {  + throw new UnsupportedOperationException("The specified platform: " + os + " is not supported.");  + }  + }  +  + public abstract JmeContext newContext(AppSettings settings, JmeContext.Type contextType);  +  + public abstract AudioRenderer newAudioRenderer(AppSettings settings);  +  + public abstract void initialize(AppSettings settings);  +}  
diff --git a/engine/src/core/com/jme3/util/BufferUtils.java b/engine/src/core/com/jme3/util/BufferUtils.java index f0cc698..2e59e97 100644 --- a/engine/src/core/com/jme3/util/BufferUtils.java +++ b/engine/src/core/com/jme3/util/BufferUtils.java 
@@ -42,6 +42,7 @@  import java.util.Collections;  import java.util.Map;  import java.util.WeakHashMap; +import java.util.concurrent.atomic.AtomicBoolean;  import java.util.logging.Level;  import java.util.logging.Logger;   @@ -1144,6 +1145,47 @@  }  }   + private static final AtomicBoolean loadedMethods = new AtomicBoolean(false); + private static Method cleanerMethod = null; + private static Method cleanMethod = null; + private static Method viewedBufferMethod = null; + private static Method freeMethod = null; +  + private static Method loadMethod(String className, String methodName){ + try { + Method method = Class.forName(className).getMethod(methodName); + method.setAccessible(true); + return method; + } catch (NoSuchMethodException ex) { + return null; // the method was not found + } catch (SecurityException ex) { + return null; // setAccessible not allowed by security policy + } catch (ClassNotFoundException ex) { + return null; // the direct buffer implementation was not found + } + } +  + private static void loadCleanerMethods() { + // If its already true, exit, if not, set it to true. + if (loadedMethods.getAndSet(true)) { + return; + } + // This could potentially be called many times if used from multiple + // threads + synchronized (loadedMethods) { + // Oracle JRE / OpenJDK + cleanerMethod = loadMethod("sun.nio.ch.DirectBuffer", "cleaner"); + cleanMethod = loadMethod("sun.misc.Cleaner", "clean"); + viewedBufferMethod = loadMethod("sun.nio.ch.DirectBuffer", "viewedBuffer"); +  + // Apache Harmony + freeMethod = loadMethod("org.apache.harmony.nio.internal.DirectBuffer", "free"); +  + // GUN Classpath (not likely) + //finalizeMethod = loadMethod("java.nio.DirectByteBufferImpl", "finalize"); + } + } +   /**  * Direct buffers are garbage collected by using a phantom reference and a  * reference queue. Every once a while, the JVM checks the reference queue and @@ -1157,27 +1199,27 @@  *   */  public static void destroyDirectBuffer(Buffer toBeDestroyed) { -   if (!toBeDestroyed.isDirect()) {  return;  } +  + loadCleanerMethods(); +   try { - Method cleanerMethod = toBeDestroyed.getClass().getMethod("cleaner"); - cleanerMethod.setAccessible(true); - Object cleaner = cleanerMethod.invoke(toBeDestroyed); - if (cleaner != null) { - Method cleanMethod = cleaner.getClass().getMethod("clean"); - cleanMethod.setAccessible(true); - cleanMethod.invoke(cleaner); + if (freeMethod != null) { + freeMethod.invoke(toBeDestroyed);  } else { - // Try the alternate approach of getting the viewed buffer - Method viewedBufferMethod = toBeDestroyed.getClass().getMethod("viewedBuffer"); - viewedBufferMethod.setAccessible(true); - Object viewedBuffer = viewedBufferMethod.invoke(toBeDestroyed); - if (viewedBuffer != null) { - destroyDirectBuffer( (Buffer)viewedBuffer ); + Object cleaner = cleanerMethod.invoke(toBeDestroyed); + if (cleaner != null) { + cleanMethod.invoke(cleaner);  } else { - Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "Buffer cannot be destroyed: {0}", toBeDestroyed); + // Try the alternate approach of getting the viewed buffer first + Object viewedBuffer = viewedBufferMethod.invoke(toBeDestroyed); + if (viewedBuffer != null) { + destroyDirectBuffer((Buffer) viewedBuffer); + } else { + Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "Buffer cannot be destroyed: {0}", toBeDestroyed); + }  }  }  } catch (IllegalAccessException ex) { @@ -1186,11 +1228,8 @@  Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);  } catch (InvocationTargetException ex) {  Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex); - } catch (NoSuchMethodException ex) { - Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);  } catch (SecurityException ex) {  Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);  }  } -  } 
diff --git a/engine/src/core/com/jme3/util/IntMap.java b/engine/src/core/com/jme3/util/IntMap.java index edf659b..1b91119 100644 --- a/engine/src/core/com/jme3/util/IntMap.java +++ b/engine/src/core/com/jme3/util/IntMap.java 
@@ -44,8 +44,6 @@  * @author Nate   */  public final class IntMap<T> implements Iterable<Entry<T>>, Cloneable { - - private final IntMapIterator iterator = new IntMapIterator();    private Entry[] table;  private final float loadFactor; @@ -200,8 +198,9 @@  }    public Iterator<Entry<T>> iterator() { - iterator.beginUse(); - return iterator; + IntMapIterator it = new IntMapIterator(); + it.beginUse(); + return it;  }    final class IntMapIterator implements Iterator<Entry<T>> { 
diff --git a/engine/src/core/com/jme3/util/TangentBinormalGenerator.java b/engine/src/core/com/jme3/util/TangentBinormalGenerator.java index 88f6822..8816b91 100644 --- a/engine/src/core/com/jme3/util/TangentBinormalGenerator.java +++ b/engine/src/core/com/jme3/util/TangentBinormalGenerator.java 
@@ -117,7 +117,13 @@  }  } else {  Geometry geom = (Geometry) scene; - generate(geom.getMesh()); + Mesh mesh = geom.getMesh(); +  + // Check to ensure mesh has texcoords and normals before generating + if (mesh.getBuffer(Type.TexCoord) != null  + && mesh.getBuffer(Type.Normal) != null){ + generate(geom.getMesh()); + }  }  }   @@ -640,7 +646,7 @@  lineMesh.setBuffer(Type.Color, 4, lineColor);    lineMesh.setStatic(); - lineMesh.setInterleaved(); + //lineMesh.setInterleaved();  return lineMesh;  }   @@ -733,7 +739,7 @@  lineMesh.setBuffer(Type.Color, 4, lineColor);    lineMesh.setStatic(); - lineMesh.setInterleaved(); + //lineMesh.setInterleaved();  return lineMesh;  }  } 
diff --git a/engine/src/desktop/com/jme3/texture/plugins/AWTLoader.java b/engine/src/desktop/com/jme3/texture/plugins/AWTLoader.java index 3f471f7..9513bcf 100644 --- a/engine/src/desktop/com/jme3/texture/plugins/AWTLoader.java +++ b/engine/src/desktop/com/jme3/texture/plugins/AWTLoader.java 
@@ -33,6 +33,7 @@  package com.jme3.texture.plugins;    import com.jme3.asset.AssetInfo; +import com.jme3.asset.AssetLoadException;  import com.jme3.asset.AssetLoader;  import com.jme3.asset.TextureKey;  import com.jme3.texture.Image; @@ -185,27 +186,30 @@  public Image load(InputStream in, boolean flipY) throws IOException{  ImageIO.setUseCache(false);  BufferedImage img = ImageIO.read(in); - if (img == null) + if (img == null){  return null; - + }  return load(img, flipY);  }    public Object load(AssetInfo info) throws IOException { - if (ImageIO.getImageWritersBySuffix(info.getKey().getExtension()) != null){ -  + if (ImageIO.getImageReadersBySuffix(info.getKey().getExtension()) != null){  boolean flip = ((TextureKey) info.getKey()).isFlipY();  InputStream in = null;  try {  in = info.openStream();  Image img = load(in, flip); + if (img == null){ + throw new AssetLoadException("The given image cannot be loaded " + info.getKey()); + }  return img;  } finally {  if (in != null){  in.close();  }  } + }else{ + throw new AssetLoadException("The extension " + info.getKey().getExtension() + " is not supported");  } - return null;  }  } 
diff --git a/engine/src/niftygui/Common/MatDefs/Nifty/Nifty.frag b/engine/src/niftygui/Common/MatDefs/Nifty/Nifty.frag deleted file mode 100644 index 5e77548..0000000 --- a/engine/src/niftygui/Common/MatDefs/Nifty/Nifty.frag +++ /dev/null 
@@ -1,13 +0,0 @@ -uniform bool m_UseTex;  -uniform sampler2D m_Texture;  -uniform vec4 m_Color;  -  -varying vec2 texCoord;  -varying vec4 color;  -  -void main() {  - vec4 texVal = texture2D(m_Texture, texCoord);  - texVal = m_UseTex ? texVal : vec4(1.0);  - gl_FragColor = texVal * color * m_Color;  -}  -  
diff --git a/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuad.frag b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuad.frag new file mode 100644 index 0000000..31f49d6 --- /dev/null +++ b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuad.frag 
@@ -0,0 +1,6 @@ +uniform vec4 m_Color;  +  +void main() {  + gl_FragColor = m_Color;  +}  +  
diff --git a/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuad.j3md b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuad.j3md new file mode 100644 index 0000000..d0213e9 --- /dev/null +++ b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuad.j3md 
@@ -0,0 +1,19 @@ +MaterialDef Default GUI {  +  + MaterialParameters {  + Vector4 Color (Color)  + }  +  + Technique {  + VertexShader GLSL100: Common/MatDefs/Nifty/NiftyQuad.vert  + FragmentShader GLSL100: Common/MatDefs/Nifty/NiftyQuad.frag  +  + WorldParameters {  + WorldViewProjectionMatrix  + }  + }  +  + Technique FixedFunc {  + }  +  +} \ No newline at end of file 
diff --git a/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuad.vert b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuad.vert new file mode 100644 index 0000000..1eb1616 --- /dev/null +++ b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuad.vert 
@@ -0,0 +1,9 @@ +uniform mat4 g_WorldViewProjectionMatrix;  +  +attribute vec4 inPosition;  +  +  +void main() {  + vec2 pos = (g_WorldViewProjectionMatrix * inPosition).xy;  + gl_Position = vec4(pos, 0.0, 1.0);  +} \ No newline at end of file 
diff --git a/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuadGrad.frag b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuadGrad.frag new file mode 100644 index 0000000..1f0a645 --- /dev/null +++ b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuadGrad.frag 
@@ -0,0 +1,6 @@ +varying vec4 color;  +  +void main() {  + gl_FragColor = color;  +}  +  
diff --git a/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuadGrad.j3md b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuadGrad.j3md new file mode 100644 index 0000000..4b3b22d --- /dev/null +++ b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuadGrad.j3md 
@@ -0,0 +1,18 @@ +MaterialDef Default GUI {  +  + MaterialParameters {  + }  +  + Technique {  + VertexShader GLSL100: Common/MatDefs/Nifty/NiftyQuadGrad.vert  + FragmentShader GLSL100: Common/MatDefs/Nifty/NiftyQuadGrad.frag  +  + WorldParameters {  + WorldViewProjectionMatrix  + }  + }  +  + Technique FixedFunc {  + }  +  +} \ No newline at end of file 
diff --git a/engine/src/niftygui/Common/MatDefs/Nifty/Nifty.vert b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuadGrad.vert similarity index 70% copy from engine/src/niftygui/Common/MatDefs/Nifty/Nifty.vert copy to engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuadGrad.vert index 67c864d..0ecaee4 100644 --- a/engine/src/niftygui/Common/MatDefs/Nifty/Nifty.vert +++ b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuadGrad.vert 
@@ -2,15 +2,13 @@    attribute vec4 inPosition;   attribute vec4 inColor;  -attribute vec2 inTexCoord;  +attribute vec4 inIndex;    -varying vec2 texCoord;   varying vec4 color;     void main() {   vec2 pos = (g_WorldViewProjectionMatrix * inPosition).xy;   gl_Position = vec4(pos, 0.0, 1.0);    - texCoord = inTexCoord;  - color = inColor;  + color = inIndex;   } \ No newline at end of file 
diff --git a/engine/src/niftygui/Common/MatDefs/Nifty/NiftyTex.frag b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyTex.frag new file mode 100644 index 0000000..b2b4b95 --- /dev/null +++ b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyTex.frag 
@@ -0,0 +1,10 @@ +uniform sampler2D m_Texture;  +uniform vec4 m_Color;  +  +varying vec2 texCoord;  +  +void main() {  + vec4 texVal = texture2D(m_Texture, texCoord);  + gl_FragColor = texVal * m_Color ;  +}  +  
diff --git a/engine/src/niftygui/Common/MatDefs/Nifty/Nifty.j3md b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyTex.j3md similarity index 62% rename from engine/src/niftygui/Common/MatDefs/Nifty/Nifty.j3md rename to engine/src/niftygui/Common/MatDefs/Nifty/NiftyTex.j3md index 9ba39b1..07f5e92 100644 --- a/engine/src/niftygui/Common/MatDefs/Nifty/Nifty.j3md +++ b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyTex.j3md 
@@ -2,13 +2,12 @@    MaterialParameters {   Texture2D Texture  - Boolean UseTex   Vector4 Color (Color)   }     Technique {  - VertexShader GLSL100: Common/MatDefs/Nifty/Nifty.vert  - FragmentShader GLSL100: Common/MatDefs/Nifty/Nifty.frag  + VertexShader GLSL100: Common/MatDefs/Nifty/NiftyTex.vert  + FragmentShader GLSL100: Common/MatDefs/Nifty/NiftyTex.frag     WorldParameters {   WorldViewProjectionMatrix  
diff --git a/engine/src/niftygui/Common/MatDefs/Nifty/Nifty.vert b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyTex.vert similarity index 72% rename from engine/src/niftygui/Common/MatDefs/Nifty/Nifty.vert rename to engine/src/niftygui/Common/MatDefs/Nifty/NiftyTex.vert index 67c864d..c5c3dc1 100644 --- a/engine/src/niftygui/Common/MatDefs/Nifty/Nifty.vert +++ b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyTex.vert 
@@ -1,16 +1,13 @@  uniform mat4 g_WorldViewProjectionMatrix;     attribute vec4 inPosition;  -attribute vec4 inColor;   attribute vec2 inTexCoord;     varying vec2 texCoord;  -varying vec4 color;     void main() {   vec2 pos = (g_WorldViewProjectionMatrix * inPosition).xy;   gl_Position = vec4(pos, 0.0, 1.0);    - texCoord = inTexCoord;  - color = inColor;  + texCoord = inTexCoord;   } \ No newline at end of file 
diff --git a/engine/src/niftygui/com/jme3/niftygui/InputSystemJme.java b/engine/src/niftygui/com/jme3/niftygui/InputSystemJme.java index 0b80131..01c2ac7 100644 --- a/engine/src/niftygui/com/jme3/niftygui/InputSystemJme.java +++ b/engine/src/niftygui/com/jme3/niftygui/InputSystemJme.java 
@@ -1,233 +1,274 @@ -/* - * Copyright (c) 2009-2010 jMonkeyEngine - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of 'jMonkeyEngine' nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.jme3.niftygui; - -import com.jme3.input.InputManager; -import com.jme3.input.KeyInput; -import com.jme3.input.RawInputListener; -import com.jme3.input.event.*; -import de.lessvoid.nifty.Nifty; -import de.lessvoid.nifty.NiftyInputConsumer; -import de.lessvoid.nifty.tools.resourceloader.NiftyResourceLoader; -import de.lessvoid.nifty.input.keyboard.KeyboardInputEvent; -import de.lessvoid.nifty.spi.input.InputSystem; -import java.util.ArrayList; - -public class InputSystemJme implements InputSystem, RawInputListener { - - private final ArrayList<InputEvent> inputQueue = new ArrayList<InputEvent>(); - - private InputManager inputManager; - - private boolean isDragging = false, niftyOwnsDragging = false; - private boolean pressed = false; - private int buttonIndex; - private int x, y; - private int height; - - private boolean shiftDown = false; - private boolean ctrlDown = false; - - private Nifty nifty; - - public InputSystemJme(InputManager inputManager){ - this.inputManager = inputManager; - } - - public void setResourceLoader(NiftyResourceLoader niftyResourceLoader) { - } - - public void setNifty(Nifty nifty) { - this.nifty = nifty; - } - - /** - * @param height The height of the viewport. Used to convert - * buttom-left origin to upper-left origin. - */ - public void setHeight(int height){ - this.height = height; - } - - public void setMousePosition(int x, int y){ - } - - public void beginInput(){ - } - - public void endInput(){ - boolean result = nifty.update(); - } - - private void onTouchEventQueued(TouchEvent evt, NiftyInputConsumer nic) {  - boolean consumed = false; - - x = (int) evt.getX(); - y = (int) (height - evt.getY()); - - switch (evt.getType()) { - case DOWN: - consumed = nic.processMouseEvent(x, y, 0, 0, false); - isDragging = true; - niftyOwnsDragging = consumed; - if (consumed){ - evt.setConsumed(); - } - - break; - - case UP: - if (niftyOwnsDragging){ - consumed = nic.processMouseEvent(x, y, 0, buttonIndex, pressed); - if (consumed){ - evt.setConsumed(); - } - } - - isDragging = false; - niftyOwnsDragging = false; - break; - } - } -  - private void onMouseMotionEventQueued(MouseMotionEvent evt, NiftyInputConsumer nic) { - x = evt.getX(); - y = height - evt.getY(); - nic.processMouseEvent(x, y, evt.getDeltaWheel(), buttonIndex, pressed); -// if (nic.processMouseEvent(niftyEvt) /*|| nifty.getCurrentScreen().isMouseOverElement()*/){ - // Do not consume motion events - //evt.setConsumed(); -// } - } - - private void onMouseButtonEventQueued(MouseButtonEvent evt, NiftyInputConsumer nic) { - boolean wasPressed = pressed; - boolean forwardToNifty = true; -  - buttonIndex = evt.getButtonIndex(); - pressed = evt.isPressed(); -  - // Mouse button raised. End dragging - if (wasPressed && !pressed){ - if (!niftyOwnsDragging){ - forwardToNifty = false; - } - isDragging = false; - niftyOwnsDragging = false; - } - - boolean consumed = false; - if (forwardToNifty){ - consumed = nic.processMouseEvent(x, y, 0, buttonIndex, pressed); - if (consumed){ - evt.setConsumed(); - } - } -  - // Mouse button pressed. Begin dragging - if (!wasPressed && pressed){ - isDragging = true; - niftyOwnsDragging = consumed; - } - } - - private void onKeyEventQueued(KeyInputEvent evt, NiftyInputConsumer nic) { - int code = evt.getKeyCode(); - - if (code == KeyInput.KEY_LSHIFT || code == KeyInput.KEY_RSHIFT) { - shiftDown = evt.isPressed(); - } else if (code == KeyInput.KEY_LCONTROL || code == KeyInput.KEY_RCONTROL) { - ctrlDown = evt.isPressed(); - } -  - KeyboardInputEvent keyEvt = new KeyboardInputEvent(code, - evt.getKeyChar(), - evt.isPressed(), - shiftDown, - ctrlDown); - - if (nic.processKeyboardEvent(keyEvt)){ - evt.setConsumed(); - } - } -  - public void onMouseMotionEvent(MouseMotionEvent evt) { - // Only forward the event if there's actual motion involved. - if (inputManager.isCursorVisible() && (evt.getDX() != 0 || - evt.getDY() != 0 || - evt.getDeltaWheel() != 0)){ - inputQueue.add(evt); - } - } - - public void onMouseButtonEvent(MouseButtonEvent evt) { - if (inputManager.isCursorVisible() && evt.getButtonIndex() >= 0 && evt.getButtonIndex() <= 2){ - inputQueue.add(evt); - } - } -  - public void onJoyAxisEvent(JoyAxisEvent evt) { - } - - public void onJoyButtonEvent(JoyButtonEvent evt) { - } -  - public void onKeyEvent(KeyInputEvent evt) { - inputQueue.add(evt); - } -  - public void onTouchEvent(TouchEvent evt) {  - inputQueue.add(evt); - } - - public void forwardEvents(NiftyInputConsumer nic) { - int queueSize = inputQueue.size(); - - for (int i = 0; i < queueSize; i++){ - InputEvent evt = inputQueue.get(i); - if (evt instanceof MouseMotionEvent){ - onMouseMotionEventQueued( (MouseMotionEvent)evt, nic); - }else if (evt instanceof MouseButtonEvent){ - onMouseButtonEventQueued( (MouseButtonEvent)evt, nic); - }else if (evt instanceof KeyInputEvent){ - onKeyEventQueued( (KeyInputEvent)evt, nic); - }else if (evt instanceof TouchEvent){ - onTouchEventQueued( (TouchEvent)evt, nic); - } - } - - inputQueue.clear(); - } -  -  -} +/*  + * Copyright (c) 2009-2010 jMonkeyEngine  + * All rights reserved.  + *  + * Redistribution and use in source and binary forms, with or without  + * modification, are permitted provided that the following conditions are  + * met:  + *  + * * Redistributions of source code must retain the above copyright  + * notice, this list of conditions and the following disclaimer.  + *  + * * Redistributions in binary form must reproduce the above copyright  + * notice, this list of conditions and the following disclaimer in the  + * documentation and/or other materials provided with the distribution.  + *  + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors  + * may be used to endorse or promote products derived from this software  + * without specific prior written permission.  + *  + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED  + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR  + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR  + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,  + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,  + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR  + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF  + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING  + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS  + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  + */  +package com.jme3.niftygui;  +  +import com.jme3.input.InputManager;  +import com.jme3.input.KeyInput;  +import com.jme3.input.RawInputListener;  +import com.jme3.input.SoftTextDialogInput;  +import com.jme3.input.controls.SoftTextDialogInputListener;  +import com.jme3.input.event.*;  +import com.jme3.system.JmeSystem;  +import de.lessvoid.nifty.Nifty;  +import de.lessvoid.nifty.NiftyInputConsumer;  +import de.lessvoid.nifty.controls.TextField;  +import de.lessvoid.nifty.controls.nullobjects.TextFieldNull;  +import de.lessvoid.nifty.elements.Element;  +import de.lessvoid.nifty.tools.resourceloader.NiftyResourceLoader;  +import de.lessvoid.nifty.input.keyboard.KeyboardInputEvent;  +import de.lessvoid.nifty.spi.input.InputSystem;  +import java.util.ArrayList;  +import java.util.logging.Level;  +import java.util.logging.Logger;  +  +public class InputSystemJme implements InputSystem, RawInputListener {  +  + private final ArrayList<InputEvent> inputQueue = new ArrayList<InputEvent>();  + private InputManager inputManager;  + private boolean isDragging = false, niftyOwnsDragging = false;  + private boolean pressed = false;  + private int buttonIndex;  + private int x, y;  + private int height;  + private boolean shiftDown = false;  + private boolean ctrlDown = false;  + private Nifty nifty;  +  + public InputSystemJme(InputManager inputManager) {  + this.inputManager = inputManager;  + }  +  + public void setResourceLoader(NiftyResourceLoader niftyResourceLoader) {  + }  +  + public void setNifty(Nifty nifty) {  + this.nifty = nifty;  + }  +  + /**  + * @param height The height of the viewport. Used to convert  + * buttom-left origin to upper-left origin.  + */  + public void setHeight(int height) {  + this.height = height;  + }  +  + public void setMousePosition(int x, int y) {  + }  +  + public void beginInput() {  + }  +  + public void endInput() {  + boolean result = nifty.update();  + }  +  + private void onTouchEventQueued(TouchEvent evt, NiftyInputConsumer nic) {  + boolean consumed = false;  +  + x = (int) evt.getX();  + y = (int) (height - evt.getY());  +  + if (!inputManager.getSimulateMouse()) {  + switch (evt.getType()) {  + case DOWN:  + consumed = nic.processMouseEvent(x, y, 0, 0, true);  + isDragging = true;  + niftyOwnsDragging = consumed;  + if (consumed) {  + evt.setConsumed();  + }  +  + break;  +  + case UP:  + if (niftyOwnsDragging) {  + consumed = nic.processMouseEvent(x, y, 0, 0, false);  + if (consumed) {  + evt.setConsumed();  + }  + }  +  + isDragging = false;  + niftyOwnsDragging = false;  +  + if (consumed) {  + processSoftKeyboard();  + }  +  + break;  + }  + }  + }  +  + private void onMouseMotionEventQueued(MouseMotionEvent evt, NiftyInputConsumer nic) {  + x = evt.getX();  + y = height - evt.getY();  + nic.processMouseEvent(x, y, evt.getDeltaWheel(), buttonIndex, pressed);  +// if (nic.processMouseEvent(niftyEvt) /*|| nifty.getCurrentScreen().isMouseOverElement()*/){  + // Do not consume motion events  + //evt.setConsumed();  +// }  + }  +  + private void onMouseButtonEventQueued(MouseButtonEvent evt, NiftyInputConsumer nic) {  + boolean wasPressed = pressed;  + boolean forwardToNifty = true;  +  + buttonIndex = evt.getButtonIndex();  + pressed = evt.isPressed();  +  + // Mouse button raised. End dragging  + if (wasPressed && !pressed) {  + if (!niftyOwnsDragging) {  + forwardToNifty = false;  + }  + isDragging = false;  + niftyOwnsDragging = false;  + }  +  + boolean consumed = false;  + if (forwardToNifty) {  + consumed = nic.processMouseEvent(x, y, 0, buttonIndex, pressed);  + if (consumed) {  + evt.setConsumed();  + }  + }  +  + // Mouse button pressed. Begin dragging  + if (!wasPressed && pressed) {  + isDragging = true;  + niftyOwnsDragging = consumed;  + }  +  + if (consumed && pressed) {  + processSoftKeyboard();  + }  +  + }  +  + private void onKeyEventQueued(KeyInputEvent evt, NiftyInputConsumer nic) {  + int code = evt.getKeyCode();  +  + if (code == KeyInput.KEY_LSHIFT || code == KeyInput.KEY_RSHIFT) {  + shiftDown = evt.isPressed();  + } else if (code == KeyInput.KEY_LCONTROL || code == KeyInput.KEY_RCONTROL) {  + ctrlDown = evt.isPressed();  + }  +  + KeyboardInputEvent keyEvt = new KeyboardInputEvent(code,  + evt.getKeyChar(),  + evt.isPressed(),  + shiftDown,  + ctrlDown);  +  + if (nic.processKeyboardEvent(keyEvt)) {  + evt.setConsumed();  + }  + }  +  + public void onMouseMotionEvent(MouseMotionEvent evt) {  + // Only forward the event if there's actual motion involved.  + if (inputManager.isCursorVisible() && (evt.getDX() != 0  + || evt.getDY() != 0  + || evt.getDeltaWheel() != 0)) {  + inputQueue.add(evt);  + }  + }  +  + public void onMouseButtonEvent(MouseButtonEvent evt) {  + if (inputManager.isCursorVisible() && evt.getButtonIndex() >= 0 && evt.getButtonIndex() <= 2) {  + inputQueue.add(evt);  + }  + }  +  + public void onJoyAxisEvent(JoyAxisEvent evt) {  + }  +  + public void onJoyButtonEvent(JoyButtonEvent evt) {  + }  +  + public void onKeyEvent(KeyInputEvent evt) {  + inputQueue.add(evt);  + }  +  + public void onTouchEvent(TouchEvent evt) {  + inputQueue.add(evt);  + }  +  + public void forwardEvents(NiftyInputConsumer nic) {  + int queueSize = inputQueue.size();  +  + for (int i = 0; i < queueSize; i++) {  + InputEvent evt = inputQueue.get(i);  + if (evt instanceof MouseMotionEvent) {  + onMouseMotionEventQueued((MouseMotionEvent) evt, nic);  + } else if (evt instanceof MouseButtonEvent) {  + onMouseButtonEventQueued((MouseButtonEvent) evt, nic);  + } else if (evt instanceof KeyInputEvent) {  + onKeyEventQueued((KeyInputEvent) evt, nic);  + } else if (evt instanceof TouchEvent) {  + onTouchEventQueued((TouchEvent) evt, nic);  + }  + }  +  + inputQueue.clear();  + }  +  + private void processSoftKeyboard() {  + SoftTextDialogInput softTextDialogInput = JmeSystem.getSoftTextDialogInput();  + if (softTextDialogInput != null) {  +  + Element element = nifty.getCurrentScreen().getFocusHandler().getKeyboardFocusElement();  + if (element != null) {  + final TextField textField = element.getNiftyControl(TextField.class);  + if (textField != null && !(textField instanceof TextFieldNull)) {  + Logger.getLogger(InputSystemJme.class.getName()).log(Level.INFO, "Current TextField: {0}", textField.getId());  + String initialValue = textField.getText();  + if (initialValue == null) {  + initialValue = "";  + }  +  + softTextDialogInput.requestDialog(SoftTextDialogInput.TEXT_ENTRY_DIALOG, "Enter Text", initialValue, new SoftTextDialogInputListener() {  +  + public void onSoftText(int action, String text) {  + if (action == SoftTextDialogInputListener.COMPLETE) {  + textField.setText(text);  + }  + }  + });  + }  + }  + }  +  + }  +}  
diff --git a/engine/src/niftygui/com/jme3/niftygui/RenderDeviceJme.java b/engine/src/niftygui/com/jme3/niftygui/RenderDeviceJme.java index 8977ed6..1f36a97 100644 --- a/engine/src/niftygui/com/jme3/niftygui/RenderDeviceJme.java +++ b/engine/src/niftygui/com/jme3/niftygui/RenderDeviceJme.java 
@@ -29,7 +29,6 @@  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.   */  -   package com.jme3.niftygui;     import com.jme3.font.BitmapText;  @@ -60,140 +59,148 @@  import java.util.HashMap;     public class RenderDeviceJme implements RenderDevice {  -  +   private NiftyJmeDisplay display;   private RenderManager rm;   private Renderer r;  -   private HashMap<String, BitmapText> textCacheLastFrame = new HashMap<String, BitmapText>();   private HashMap<String, BitmapText> textCacheCurrentFrame = new HashMap<String, BitmapText>();  -   private final Quad quad = new Quad(1, -1, true);   private final Geometry quadGeom = new Geometry("nifty-quad", quad);   private final Material niftyMat;  -  + private final Material niftyQuadMat;  + private final Material niftyQuadGradMat;   private boolean clipWasSet = false;   private BlendMode blendMode = null;  -   private VertexBuffer quadDefaultTC = quad.getBuffer(Type.TexCoord);   private VertexBuffer quadModTC = quadDefaultTC.clone();   private VertexBuffer quadColor;  -   private Matrix4f tempMat = new Matrix4f();   private ColorRGBA tempColor = new ColorRGBA();  -  - public RenderDeviceJme(NiftyJmeDisplay display){  +  + public RenderDeviceJme(NiftyJmeDisplay display) {   this.display = display;  -  +   quadColor = new VertexBuffer(Type.Color);   quadColor.setNormalized(true);   ByteBuffer bb = BufferUtils.createByteBuffer(4 * 4);   quadColor.setupData(Usage.Stream, 4, Format.UnsignedByte, bb);   quad.setBuffer(quadColor);  -  +   quadModTC.setUsage(Usage.Stream);  -  - niftyMat = new Material(display.getAssetManager(), "Common/MatDefs/Nifty/Nifty.j3md");  +  + //Color + texture color material for text and images  + niftyMat = new Material(display.getAssetManager(), "Common/MatDefs/Nifty/NiftyTex.j3md");   niftyMat.getAdditionalRenderState().setDepthTest(false);  + //Color material for uniform colored quads  + niftyQuadMat = new Material(display.getAssetManager(), "Common/MatDefs/Nifty/NiftyQuad.j3md");  + niftyQuadMat.getAdditionalRenderState().setDepthTest(false);  +  + //vertex color only for gradient quads (although i didn't find a way in nifty to make a gradient using vertex color)  + niftyQuadGradMat = new Material(display.getAssetManager(), "Common/MatDefs/Nifty/NiftyQuadGrad.j3md");  + niftyQuadGradMat.getAdditionalRenderState().setDepthTest(false);  +   }  -  +   public void setResourceLoader(NiftyResourceLoader niftyResourceLoader) {   }  -  - public void setRenderManager(RenderManager rm){  +  + public void setRenderManager(RenderManager rm) {   this.rm = rm;   this.r = rm.getRenderer();   }     // TODO: Cursor support  - public MouseCursor createMouseCursor(String str, int x, int y){  + public MouseCursor createMouseCursor(String str, int x, int y) {   return new MouseCursor() {  +   public void dispose() {   }   };   }  -  - public void enableMouseCursor(MouseCursor cursor){  +  + public void enableMouseCursor(MouseCursor cursor) {   }  -  - public void disableMouseCursor(){  +  + public void disableMouseCursor() {   }  -  +   public RenderImage createImage(String filename, boolean linear) {   return new RenderImageJme(filename, linear, display);   }  -  +   public RenderFont createFont(String filename) {   return new RenderFontJme(filename, display);   }  -  +   public void beginFrame() {   }  -  +   public void endFrame() {   HashMap<String, BitmapText> temp = textCacheLastFrame;   textCacheLastFrame = textCacheCurrentFrame;   textCacheCurrentFrame = temp;   textCacheCurrentFrame.clear();  -  +   // System.exit(1);   }  -  +   public int getWidth() {   return display.getWidth();   }  -  +   public int getHeight() {   return display.getHeight();   }  -  +   public void clear() {   }  -  +   public void setBlendMode(BlendMode blendMode) {  - if (this.blendMode != blendMode){  + if (this.blendMode != blendMode) {   this.blendMode = blendMode;   }   }  -  - private RenderState.BlendMode convertBlend(){  - if (blendMode == null)  +  + private RenderState.BlendMode convertBlend() {  + if (blendMode == null) {   return RenderState.BlendMode.Off;  - else if (blendMode == BlendMode.BLEND)  + } else if (blendMode == BlendMode.BLEND) {   return RenderState.BlendMode.Alpha;  - else if (blendMode == BlendMode.MULIPLY)  + } else if (blendMode == BlendMode.MULIPLY) {   return RenderState.BlendMode.Modulate;  - else  + } else {   throw new UnsupportedOperationException();  - }  -  - private int convertColor(Color color){  - int color2 = 0;  - color2 |= ((int)(255.0 * color.getAlpha())) << 24;  - color2 |= ((int)(255.0 * color.getBlue())) << 16;  - color2 |= ((int)(255.0 * color.getGreen())) << 8;  - color2 |= ((int)(255.0 * color.getRed()));  - return color2;  - }  -  - private ColorRGBA convertColor(Color inColor, ColorRGBA outColor){  - return outColor.set(inColor.getRed(), inColor.getGreen(), inColor.getBlue(), inColor.getAlpha());  - }  -  - private void setColor(Color color){  - ByteBuffer buf = (ByteBuffer) quadColor.getData();  - buf.rewind();  -  - int color2 = convertColor(color);  - buf.putInt(color2);  - buf.putInt(color2);  - buf.putInt(color2);  - buf.putInt(color2);  -  - buf.flip();  - quadColor.updateData(buf);  + }   }    + private int convertColor(Color color) {  + int color2 = 0;  + color2 |= ((int) (255.0 * color.getAlpha())) << 24;  + color2 |= ((int) (255.0 * color.getBlue())) << 16;  + color2 |= ((int) (255.0 * color.getGreen())) << 8;  + color2 |= ((int) (255.0 * color.getRed()));  + return color2;  + }  +  + private ColorRGBA convertColor(Color inColor, ColorRGBA outColor) {  + return outColor.set(inColor.getRed(), inColor.getGreen(), inColor.getBlue(), inColor.getAlpha());  + }  +  +// private void setColor(Color color) {  +// ByteBuffer buf = (ByteBuffer) quadColor.getData();  +// buf.rewind();  +//  +// int color2 = convertColor(color);  +// buf.putInt(color2);  +// buf.putInt(color2);  +// buf.putInt(color2);  +// buf.putInt(color2);  +//  +// buf.flip();  +// quadColor.updateData(buf);  +// }  +   /**   *   * @param font  @@ -205,22 +212,23 @@  * @deprecated use renderFont(RenderFont font, String str, int x, int y, Color color, float sizeX, float sizeY) instead   */   @Deprecated  - public void renderFont(RenderFont font, String str, int x, int y, Color color, float size){  + public void renderFont(RenderFont font, String str, int x, int y, Color color, float size) {   renderFont(font, str, x, y, color, size, size);   }  -  +   @Override  - public void renderFont(RenderFont font, String str, int x, int y, Color color, float sizeX, float sizeY){  - //TODO find out what the f1 param is for  - if (str.length() == 0)  + public void renderFont(RenderFont font, String str, int x, int y, Color color, float sizeX, float sizeY) {  + if (str.length() == 0) {   return;  -  - if (font instanceof RenderFontNull)  + }  +  + if (font instanceof RenderFontNull) {   return;  -  + }  +   RenderFontJme jmeFont = (RenderFontJme) font;    - String key = font+str+color.getColorString();  + String key = font + str + color.getColorString();   BitmapText text = textCacheLastFrame.get(key);   if (text == null) {   text = jmeFont.createText();  @@ -228,166 +236,149 @@  text.updateLogicalState(0);   }   textCacheCurrentFrame.put(key, text);  -  - niftyMat.setColor("Color", convertColor(color, tempColor));  - niftyMat.setBoolean("UseTex", true);  +  + niftyMat.setColor("Color", convertColor(color, tempColor));   niftyMat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);   // niftyMat.getAdditionalRenderState().setBlendMode(convertBlend());   text.setMaterial(niftyMat);  -  - float width = text.getLineWidth();  - float height = text.getLineHeight();  -  - float x0 = x + 0.5f * width * (1f - sizeX);  - float y0 = y + 0.5f * height * (1f - sizeY);  -  +   tempMat.loadIdentity();  - tempMat.setTranslation(x0, getHeight() - y0, 0);  + tempMat.setTranslation(x, getHeight() - y, 0);   tempMat.setScale(sizeX, sizeY, 0);  -  +   rm.setWorldMatrix(tempMat);   text.render(rm);  -  +   // System.out.println("renderFont");   }  -  +   public void renderImage(RenderImage image, int x, int y, int w, int h,  - int srcX, int srcY, int srcW, int srcH,  - Color color, float scale,  - int centerX, int centerY){  + int srcX, int srcY, int srcW, int srcH,  + Color color, float scale,  + int centerX, int centerY) {   RenderImageJme jmeImage = (RenderImageJme) image;   Texture2D texture = jmeImage.getTexture();  -  +   niftyMat.getAdditionalRenderState().setBlendMode(convertBlend());  - niftyMat.setColor("Color", ColorRGBA.White);  - niftyMat.setTexture("Texture", texture);  - niftyMat.setBoolean("UseTex", true);  - setColor(color);  -  - float imageWidth = jmeImage.getWidth();  + niftyMat.setColor("Color", convertColor(color, tempColor));  + niftyMat.setTexture("Texture", texture);  + //setColor(color);  +  + float imageWidth = jmeImage.getWidth();   float imageHeight = jmeImage.getHeight();   FloatBuffer texCoords = (FloatBuffer) quadModTC.getData();  -  +   float startX = srcX / imageWidth;   float startY = srcY / imageHeight;  - float endX = startX + (srcW / imageWidth);  - float endY = startY + (srcH / imageHeight);  -  + float endX = startX + (srcW / imageWidth);  + float endY = startY + (srcH / imageHeight);  +   startY = 1f - startY;  - endY = 1f - endY;  -  + endY = 1f - endY;  +   texCoords.rewind();   texCoords.put(startX).put(startY);  - texCoords.put(endX) .put(startY);  - texCoords.put(endX) .put(endY);  + texCoords.put(endX).put(startY);  + texCoords.put(endX).put(endY);   texCoords.put(startX).put(endY);   texCoords.flip();   quadModTC.updateData(texCoords);  -  +   quad.clearBuffer(Type.TexCoord);   quad.setBuffer(quadModTC);  -  +   float x0 = centerX + (x - centerX) * scale;   float y0 = centerY + (y - centerY) * scale;  -  +   tempMat.loadIdentity();   tempMat.setTranslation(x0, getHeight() - y0, 0);   tempMat.setScale(w * scale, h * scale, 0);  -  +   rm.setWorldMatrix(tempMat);   niftyMat.render(quadGeom, rm);   //   // System.out.println("renderImage (Sub)");   }  -  +   public void renderImage(RenderImage image, int x, int y, int width, int height,  - Color color, float imageScale){  -  + Color color, float imageScale) {  +   RenderImageJme jmeImage = (RenderImageJme) image;  -  +   niftyMat.getAdditionalRenderState().setBlendMode(convertBlend());  - niftyMat.setColor("Color", ColorRGBA.White);  - niftyMat.setTexture("Texture", jmeImage.getTexture());  - niftyMat.setBoolean("UseTex", true);  - setColor(color);  -  + niftyMat.setColor("Color", convertColor(color, tempColor));  + niftyMat.setTexture("Texture", jmeImage.getTexture());  + //setColor(color);  +   quad.clearBuffer(Type.TexCoord);   quad.setBuffer(quadDefaultTC);  -  - float x0 = x + 0.5f * width * (1f - imageScale);  +  + float x0 = x + 0.5f * width * (1f - imageScale);   float y0 = y + 0.5f * height * (1f - imageScale);  -  +   tempMat.loadIdentity();   tempMat.setTranslation(x0, getHeight() - y0, 0);   tempMat.setScale(width * imageScale, height * imageScale, 0);  -  +   rm.setWorldMatrix(tempMat);   niftyMat.render(quadGeom, rm);   //   // System.out.println("renderImage");   }  -  - public void renderQuad(int x, int y, int width, int height, Color color){  - niftyMat.getAdditionalRenderState().setBlendMode(convertBlend());  - niftyMat.setColor("Color", ColorRGBA.White);  - niftyMat.clearParam("Texture");  - niftyMat.setBoolean("UseTex", false);  - setColor(color);  -  - tempMat.loadIdentity();  - tempMat.setTranslation(x, getHeight() - y, 0);  - tempMat.setScale(width, height, 0);  -  - rm.setWorldMatrix(tempMat);  - niftyMat.render(quadGeom, rm);  -  +  + public void renderQuad(int x, int y, int width, int height, Color color) {  + if (color.getAlpha() > 0) {  + niftyQuadMat.getAdditionalRenderState().setBlendMode(convertBlend());  + niftyQuadMat.setColor("Color", convertColor(color, tempColor));  +  + tempMat.loadIdentity();  + tempMat.setTranslation(x, getHeight() - y, 0);  + tempMat.setScale(width, height, 0);  +  + rm.setWorldMatrix(tempMat);  + niftyQuadMat.render(quadGeom, rm);  + }   // System.out.println("renderQuad (Solid)");   }  -  +   public void renderQuad(int x, int y, int width, int height,  - Color topLeft, Color topRight, Color bottomRight, Color bottomLeft) {  + Color topLeft, Color topRight, Color bottomRight, Color bottomLeft) {     ByteBuffer buf = (ByteBuffer) quadColor.getData();   buf.rewind();     buf.putInt(convertColor(topRight));   buf.putInt(convertColor(topLeft));  -  +   buf.putInt(convertColor(bottomLeft));   buf.putInt(convertColor(bottomRight));     buf.flip();   quadColor.updateData(buf);    - niftyMat.getAdditionalRenderState().setBlendMode(convertBlend());  - niftyMat.setColor("Color", ColorRGBA.White);  - niftyMat.clearParam("Texture");  - niftyMat.setBoolean("UseTex", false);  -  + niftyQuadGradMat.getAdditionalRenderState().setBlendMode(convertBlend());  +   tempMat.loadIdentity();   tempMat.setTranslation(x, getHeight() - y, 0);   tempMat.setScale(width, height, 0);  -  +   rm.setWorldMatrix(tempMat);  - niftyMat.render(quadGeom, rm);  + niftyQuadGradMat.render(quadGeom, rm);   //   // System.out.println("renderQuad (Grad)");   }  -  - public void enableClip(int x0, int y0, int x1, int y1){  +  + public void enableClip(int x0, int y0, int x1, int y1) {   // System.out.println("enableClip");   clipWasSet = true;   r.setClipRect(x0, getHeight() - y1, x1 - x0, y1 - y0);   }  -  +   public void disableClip() {   // System.out.println("disableClip");  - if (clipWasSet){  + if (clipWasSet) {   r.clearClipRect();   clipWasSet = false;   }   }  -  -  -   }  
diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshLoader.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshLoader.java index e7f4b10..fc8cc6b 100644 --- a/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshLoader.java +++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshLoader.java 
@@ -712,9 +712,8 @@  } else if (qName.equals("geometry")  || qName.equals("sharedgeometry")) {  // finish writing to buffers - IntMap<VertexBuffer> bufs = mesh.getBuffers(); - for (Entry<VertexBuffer> entry : bufs) { - Buffer data = entry.getValue().getData(); + for (VertexBuffer buf : mesh.getBufferList().getArray()) { + Buffer data = buf.getData();  if (data.position() != 0) {  data.flip();  } 
diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/LODGeomap.java b/engine/src/terrain/com/jme3/terrain/geomipmap/LODGeomap.java index 3944b98..3348b48 100644 --- a/engine/src/terrain/com/jme3/terrain/geomipmap/LODGeomap.java +++ b/engine/src/terrain/com/jme3/terrain/geomipmap/LODGeomap.java 
@@ -34,6 +34,7 @@  import com.jme3.export.JmeExporter;   import com.jme3.export.JmeImporter;   import com.jme3.math.FastMath;  +import com.jme3.math.Plane;   import com.jme3.math.Triangle;   import com.jme3.math.Vector2f;   import com.jme3.math.Vector3f;  @@ -87,7 +88,7 @@  IntBuffer ib = writeIndexArrayLodDiff(null, lod, rightLod, topLod, leftLod, bottomLod);   FloatBuffer bb = BufferUtils.createFloatBuffer(getWidth() * getHeight() * 3);   FloatBuffer tanb = BufferUtils.createFloatBuffer(getWidth() * getHeight() * 3);  - writeTangentArray(tanb, bb, texb, scale);  + writeTangentArray(nb, tanb, bb, texb, scale);   Mesh m = new Mesh();   m.setMode(Mode.TriangleStrip);   m.setBuffer(Type.Position, 3, pb);  @@ -627,7 +628,7 @@  return num;   }    - public FloatBuffer[] writeTangentArray(FloatBuffer tangentStore, FloatBuffer binormalStore, FloatBuffer textureBuffer, Vector3f scale) {  + public FloatBuffer[] writeTangentArray(FloatBuffer normalBuffer, FloatBuffer tangentStore, FloatBuffer binormalStore, FloatBuffer textureBuffer, Vector3f scale) {   if (!isLoaded()) {   throw new NullPointerException();   }  @@ -650,19 +651,31 @@  }   binormalStore.rewind();    + Vector3f normal = new Vector3f();   Vector3f tangent = new Vector3f();   Vector3f binormal = new Vector3f();  - Vector3f v1 = new Vector3f();  + /*Vector3f v1 = new Vector3f();   Vector3f v2 = new Vector3f();   Vector3f v3 = new Vector3f();   Vector2f t1 = new Vector2f();   Vector2f t2 = new Vector2f();  - Vector2f t3 = new Vector2f();  -  - //scale = Vector3f.UNIT_XYZ;  + Vector2f t3 = new Vector2f();*/     for (int r = 0; r < getHeight(); r++) {   for (int c = 0; c < getWidth(); c++) {  +  + int idx = (r * getWidth() + c) * 3;  + normal.set(normalBuffer.get(idx), normalBuffer.get(idx+1), normalBuffer.get(idx+2));  + tangent.set(normal.cross(new Vector3f(0,0,1)));  + binormal.set(new Vector3f(1,0,0).cross(normal));  +  + BufferUtils.setInBuffer(tangent.normalizeLocal(), tangentStore, (r * getWidth() + c)); // save the tangent  + BufferUtils.setInBuffer(binormal.normalizeLocal(), binormalStore, (r * getWidth() + c)); // save the binormal  + }  + }  +  +/* for (int r = 0; r < getHeight(); r++) {  + for (int c = 0; c < getWidth(); c++) {     int texIdx = ((getHeight() - 1 - r) * getWidth() + c) * 2; // pull from the end   int texIdxAbove = ((getHeight() - 1 - (r - 1)) * getWidth() + c) * 2; // pull from the end  @@ -702,7 +715,7 @@  BufferUtils.setInBuffer(binormal, binormalStore, (r * getWidth() + c)); // save the binormal   }   }  -  + */   return new FloatBuffer[]{tangentStore, binormalStore};   }    
diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainPatch.java b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainPatch.java index 40cf190..83537af 100644 --- a/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainPatch.java +++ b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainPatch.java 
@@ -314,13 +314,25 @@  getMesh().getBuffer(Type.Normal).updateData(newNormalBuffer);   FloatBuffer newTangentBuffer = null;   FloatBuffer newBinormalBuffer = null;  - FloatBuffer[] tb = geomap.writeTangentArray(newTangentBuffer, newBinormalBuffer, (FloatBuffer)getMesh().getBuffer(Type.TexCoord).getData(), getWorldScale());  + FloatBuffer[] tb = geomap.writeTangentArray(newNormalBuffer, newTangentBuffer, newBinormalBuffer, (FloatBuffer)getMesh().getBuffer(Type.TexCoord).getData(), getWorldScale());   newTangentBuffer = tb[0];   newBinormalBuffer = tb[1];   getMesh().getBuffer(Type.Tangent).updateData(newTangentBuffer);   getMesh().getBuffer(Type.Binormal).updateData(newBinormalBuffer);   }    + private void setInBuffer(Mesh mesh, int index, Vector3f normal, Vector3f tangent, Vector3f binormal) {  + VertexBuffer NB = mesh.getBuffer(Type.Normal);  + VertexBuffer TB = mesh.getBuffer(Type.Tangent);  + VertexBuffer BB = mesh.getBuffer(Type.Binormal);  + BufferUtils.setInBuffer(normal, (FloatBuffer)NB.getData(), index);  + BufferUtils.setInBuffer(tangent, (FloatBuffer)TB.getData(), index);  + BufferUtils.setInBuffer(binormal, (FloatBuffer)BB.getData(), index);  + NB.setUpdateNeeded();  + TB.setUpdateNeeded();  + BB.setUpdateNeeded();  + }  +   /**   * Matches the normals along the edge of the patch with the neighbours.   * Computes the normals for the right, bottom, left, and top edges of the  @@ -357,158 +369,122 @@  Vector3f binormal = new Vector3f();   Vector3f normal = new Vector3f();    +   int s = this.getSize()-1;     if (right != null) { // right side, works its way down   for (int i=0; i<s+1; i++) {  - rootPoint.set(s, this.getHeightmapHeight(s,i), i);  - leftPoint.set(s-1, this.getHeightmapHeight(s-1,i), i);  - rightPoint.set(s+1, right.getHeightmapHeight(1,i), i);  + rootPoint.set(0, this.getHeightmapHeight(s,i), 0);  + leftPoint.set(-1, this.getHeightmapHeight(s-1,i), 0);  + rightPoint.set(1, right.getHeightmapHeight(1,i), 0);     if (i == 0) { // top point  + bottomPoint.set(0, this.getHeightmapHeight(s,i+1), 1);  +   if (top == null) {  - bottomPoint.set(s, this.getHeightmapHeight(s,i+1), i+1);  -  - averageNormalsTangents(null, rootPoint, leftPoint, bottomPoint, rightPoint, null, null, null, null, null, normal, tangent, binormal);  - VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);  - VertexBuffer rightNB = right.getMesh().getBuffer(Type.Normal);  - BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), s);  - BufferUtils.setInBuffer(normal, (FloatBuffer)rightNB.getData(), 0);  + averageNormalsTangents(null, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);  + setInBuffer(this.getMesh(), s, normal, tangent, binormal);  + setInBuffer(right.getMesh(), 0, normal, tangent, binormal);   } else {  - topPoint.set(s, top.getHeightmapHeight(s,s-1), i-1);  - bottomPoint.set(s, this.getHeightmapHeight(s,i+1), i+1);  + topPoint.set(0, top.getHeightmapHeight(s,s-1), -1);    - averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, null, null, null, null, null, normal, tangent, binormal);  - VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);  - VertexBuffer rightNB = right.getMesh().getBuffer(Type.Normal);  - BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), s);  - BufferUtils.setInBuffer(normal, (FloatBuffer)rightNB.getData(), 0);  + averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint,normal, tangent, binormal);  + setInBuffer(this.getMesh(), s, normal, tangent, binormal);  + setInBuffer(right.getMesh(), 0, normal, tangent, binormal);  + setInBuffer(top.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal);     if (topRight != null) {  - VertexBuffer topRightNB = topRight.getMesh().getBuffer(Type.Normal);  - BufferUtils.setInBuffer(normal, (FloatBuffer)topRightNB.getData(), (s+1)*s);  - topRightNB.setUpdateNeeded();  + // setInBuffer(topRight.getMesh(), (s+1)*s, normal, tangent, binormal);   }   }   } else if (i == s) { // bottom point  + topPoint.set(0, this.getHeightmapHeight(s,s-1), -1);  +   if (bottom == null) {  - topPoint.set(s, this.getHeightmapHeight(s,i-1), i-1);  -  - averageNormalsTangents(topPoint, rootPoint, leftPoint, null, rightPoint, null, null, null, null, null, normal, tangent, binormal);  - VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);  - VertexBuffer rightNB = right.getMesh().getBuffer(Type.Normal);  - BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*(i+1)-1);  - BufferUtils.setInBuffer(normal, (FloatBuffer)rightNB.getData(), (s+1)*(s));  + averageNormalsTangents(topPoint, rootPoint, leftPoint, null, rightPoint, normal, tangent, binormal);  + setInBuffer(this.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal);  + setInBuffer(right.getMesh(), (s+1)*(s), normal, tangent, binormal);   } else {  - topPoint.set(s, this.getHeightmapHeight(s,i-1), i-1);  - bottomPoint.set(s, bottom.getHeightmapHeight(s,1), i+1);  - averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, null, null, null, null, null, normal, tangent, binormal);  - VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);  - VertexBuffer rightNB = right.getMesh().getBuffer(Type.Normal);  - VertexBuffer downNB = bottom.getMesh().getBuffer(Type.Normal);  -  - BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*(s+1)-1);  - BufferUtils.setInBuffer(normal, (FloatBuffer)rightNB.getData(), (s+1)*s);  - BufferUtils.setInBuffer(normal, (FloatBuffer)downNB.getData(), s);  + bottomPoint.set(0, bottom.getHeightmapHeight(s,1), 1);  + averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);  + setInBuffer(this.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal);  + setInBuffer(right.getMesh(), (s+1)*s, normal, tangent, binormal);  + setInBuffer(bottom.getMesh(), s, normal, tangent, binormal);     if (bottomRight != null) {  - VertexBuffer bottomRightNB = bottomRight.getMesh().getBuffer(Type.Normal);  - BufferUtils.setInBuffer(normal, (FloatBuffer)bottomRightNB.getData(), 0);  - bottomRightNB.setUpdateNeeded();  + // setInBuffer(bottomRight.getMesh(), 0, normal, tangent, binormal);   }  - downNB.setUpdateNeeded();   }   } else { // all in the middle  - topPoint.set(s, this.getHeightmapHeight(s,i-1), i-1);  - bottomPoint.set(s, this.getHeightmapHeight(s,i+1), i+1);  - averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, null, null, null, null, null, normal, tangent, binormal);  - VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);  - VertexBuffer rightNB = right.getMesh().getBuffer(Type.Normal);  - BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*(i+1)-1);  - BufferUtils.setInBuffer(normal, (FloatBuffer)rightNB.getData(), (s+1)*(i));  + topPoint.set(0, this.getHeightmapHeight(s,i-1), -1);  + bottomPoint.set(0, this.getHeightmapHeight(s,i+1), 1);  + averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);  + setInBuffer(this.getMesh(), (s+1)*(i+1)-1, normal, tangent, binormal);  + setInBuffer(right.getMesh(), (s+1)*(i), normal, tangent, binormal);   }   }  - right.getMesh().getBuffer(Type.Normal).setUpdateNeeded();   }     if (left != null) { // left side, works its way down   for (int i=0; i<s+1; i++) {  - rootPoint.set(0, this.getHeightmapHeight(0,i), i);  - leftPoint.set(-1, left.getHeightmapHeight(s-1,i), i);  - rightPoint.set(1, this.getHeightmapHeight(1,i), i);  + rootPoint.set(0, this.getHeightmapHeight(0,i), 0);  + leftPoint.set(-1, left.getHeightmapHeight(s-1,i), 0);  + rightPoint.set(1, this.getHeightmapHeight(1,i), 0);     if (i == 0) { // top point  + bottomPoint.set(0, this.getHeightmapHeight(0,i+1), 1);  +   if (top == null) {  - bottomPoint.set(0, this.getHeightmapHeight(0,i+1), i+1);  - averageNormalsTangents(null, rootPoint, leftPoint, bottomPoint, rightPoint, null, null, null, null, null, normal, tangent, binormal);  - VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);  - VertexBuffer leftNB = left.getMesh().getBuffer(Type.Normal);  - BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), 0);  - BufferUtils.setInBuffer(normal, (FloatBuffer)leftNB.getData(), s);  + averageNormalsTangents(null, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);  + setInBuffer(this.getMesh(), 0, normal, tangent, binormal);  + setInBuffer(left.getMesh(), s, normal, tangent, binormal);   } else {  - topPoint.set(0, top.getHeightmapHeight(0,s-1), i-1);  - bottomPoint.set(0, this.getHeightmapHeight(0,i+1), i+1);  + topPoint.set(0, top.getHeightmapHeight(0,s-1), -1);    - averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, null, null, null, null, null, normal, tangent, binormal);  - VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);  - VertexBuffer leftNB = left.getMesh().getBuffer(Type.Normal);  - BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), 0);  - BufferUtils.setInBuffer(normal, (FloatBuffer)leftNB.getData(), s);  + averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);  + setInBuffer(this.getMesh(), 0, normal, tangent, binormal);  + setInBuffer(left.getMesh(), s, normal, tangent, binormal);  + setInBuffer(top.getMesh(), (s+1)*s, normal, tangent, binormal);     if (topLeft != null) {  - VertexBuffer topLeftNB = topLeft.getMesh().getBuffer(Type.Normal);  - BufferUtils.setInBuffer(normal, (FloatBuffer)topLeftNB.getData(), (s+1)*(s+1)-1);  - topLeftNB.setUpdateNeeded();  + // setInBuffer(topLeft.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal);   }   }   } else if (i == s) { // bottom point  + topPoint.set(0, this.getHeightmapHeight(0,i-1), -1);  +   if (bottom == null) {  - topPoint.set(0, this.getHeightmapHeight(0,i-1), i-1);  -  - averageNormalsTangents(topPoint, rootPoint, leftPoint, null, rightPoint, null, null, null, null, null, normal, tangent, binormal);  - VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);  - VertexBuffer leftNB = left.getMesh().getBuffer(Type.Normal);  - BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*(s));  - BufferUtils.setInBuffer(normal, (FloatBuffer)leftNB.getData(), (s+1)*(i+1)-1);  + averageNormalsTangents(topPoint, rootPoint, leftPoint, null, rightPoint, normal, tangent, binormal);  + setInBuffer(this.getMesh(), (s+1)*(s), normal, tangent, binormal);  + setInBuffer(left.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal);   } else {  - topPoint.set(0, this.getHeightmapHeight(0,i-1), i-1);  - bottomPoint.set(0, bottom.getHeightmapHeight(0,1), i+1);  + bottomPoint.set(0, bottom.getHeightmapHeight(0,1), 1);    - averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, null, null, null, null, null, normal, tangent, binormal);  - VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);  - VertexBuffer leftNB = left.getMesh().getBuffer(Type.Normal);  - VertexBuffer downNB = bottom.getMesh().getBuffer(Type.Normal);  -  - BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*(s));  - BufferUtils.setInBuffer(normal, (FloatBuffer)leftNB.getData(), (s+1)*(i+1)-1);  - BufferUtils.setInBuffer(normal, (FloatBuffer)downNB.getData(), 0);  + averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);  + setInBuffer(this.getMesh(), (s+1)*(s), normal, tangent, binormal);  + setInBuffer(left.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal);  + setInBuffer(bottom.getMesh(), 0, normal, tangent, binormal);     if (bottomLeft != null) {  - VertexBuffer bottomLeftNB = bottomLeft.getMesh().getBuffer(Type.Normal);  - BufferUtils.setInBuffer(normal, (FloatBuffer)bottomLeftNB.getData(), s);  - bottomLeftNB.setUpdateNeeded();  + // setInBuffer(bottomLeft.getMesh(), s, normal, tangent, binormal);   }  - downNB.setUpdateNeeded();   }   } else { // all in the middle  - topPoint.set(0, this.getHeightmapHeight(0,i-1), i-1);  - bottomPoint.set(0, this.getHeightmapHeight(0,i+1), i+1);  + topPoint.set(0, this.getHeightmapHeight(0,i-1), -1);  + bottomPoint.set(0, this.getHeightmapHeight(0,i+1), 1);    - averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, null, null, null, null, null, normal, tangent, binormal);  - VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);  - VertexBuffer leftNB = left.getMesh().getBuffer(Type.Normal);  - BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*(i));  - BufferUtils.setInBuffer(normal, (FloatBuffer)leftNB.getData(), (s+1)*(i+1)-1);  + averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);  + setInBuffer(this.getMesh(), (s+1)*(i), normal, tangent, binormal);  + setInBuffer(left.getMesh(), (s+1)*(i+1)-1, normal, tangent, binormal);   }   }  - left.getMesh().getBuffer(Type.Normal).setUpdateNeeded();   }  -  +   if (top != null) { // top side, works its way right   for (int i=0; i<s+1; i++) {  - rootPoint.set(i, this.getHeightmapHeight(i,0), 0);  - topPoint.set(i, top.getHeightmapHeight(i,s-1), -1);  - bottomPoint.set(i, this.getHeightmapHeight(i,1), 1);  + rootPoint.set(0, this.getHeightmapHeight(i,0), 0);  + topPoint.set(0, top.getHeightmapHeight(i,s-1), -1);  + bottomPoint.set(0, this.getHeightmapHeight(i,1), 1);     if (i == 0) { // left corner   // handled by left side pass  @@ -518,49 +494,39 @@  // handled by this patch when it does its right side     } else { // all in the middle  - leftPoint.set(i-1, this.getHeightmapHeight(i-1,0), 0);  - rightPoint.set(i+1, this.getHeightmapHeight(i+1,0), 0);  - averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, null, null, null, null, null, normal, tangent, binormal);  - VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);  - BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), i);  - VertexBuffer topNB = top.getMesh().getBuffer(Type.Normal);  - BufferUtils.setInBuffer(normal, (FloatBuffer)topNB.getData(), (s+1)*(s)+i);  + leftPoint.set(-1, this.getHeightmapHeight(i-1,0), 0);  + rightPoint.set(1, this.getHeightmapHeight(i+1,0), 0);  + averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);  + setInBuffer(this.getMesh(), i, normal, tangent, binormal);  + setInBuffer(top.getMesh(), (s+1)*(s)+i, normal, tangent, binormal);   }   }  - top.getMesh().getBuffer(Type.Normal).setUpdateNeeded();     }     if (bottom != null) { // bottom side, works its way right   for (int i=0; i<s+1; i++) {  - rootPoint.set(i, this.getHeightmapHeight(i,s), s);  - topPoint.set(i, this.getHeightmapHeight(i,s-1), s-1);  - bottomPoint.set(i, bottom.getHeightmapHeight(i,1), s+1);  + rootPoint.set(0, this.getHeightmapHeight(i,s), 0);  + topPoint.set(0, this.getHeightmapHeight(i,s-1), -1);  + bottomPoint.set(0, bottom.getHeightmapHeight(i,1), 1);     if (i == 0) { // left   // handled by the left side pass     } else if (i == s) { // right    - // handled by this patch when it does its right side  + // handled by the right side pass     } else { // all in the middle  - leftPoint.set(i-1, this.getHeightmapHeight(i-1,s), s);  - rightPoint.set(i+1, this.getHeightmapHeight(i+1,s), s);  - averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, null, null, null, null, null, normal, tangent, binormal);  - VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);  - BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*(s)+i);  - VertexBuffer downNB = bottom.getMesh().getBuffer(Type.Normal);  - BufferUtils.setInBuffer(normal, (FloatBuffer)downNB.getData(), i);  + leftPoint.set(-1, this.getHeightmapHeight(i-1,s), 0);  + rightPoint.set(1, this.getHeightmapHeight(i+1,s), 0);  + averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);  + setInBuffer(this.getMesh(), (s+1)*(s)+i, normal, tangent, binormal);  + setInBuffer(bottom.getMesh(), i, normal, tangent, binormal);   }   }  - bottom.getMesh().getBuffer(Type.Normal).setUpdateNeeded();     }  -  - this.getMesh().getBuffer(Type.Normal).setUpdateNeeded();  - this.getMesh().getBuffer(Type.Tangent).setUpdateNeeded();  - this.getMesh().getBuffer(Type.Binormal).setUpdateNeeded();   }     protected void averageNormalsTangents(  @@ -569,38 +535,36 @@  Vector3f leftPoint,   Vector3f bottomPoint,   Vector3f rightPoint,  - Vector2f topTex,  - Vector2f rootTex,  - Vector2f leftTex,  - Vector2f bottomTex,  - Vector2f rightTex,   Vector3f normal,   Vector3f tangent,   Vector3f binormal)   {   Vector3f scale = getWorldScale();    - Vector3f n1 = Vector3f.ZERO;  + Vector3f n1 = new Vector3f(0,0,0);   if (topPoint != null && leftPoint != null) {  - n1 = calculateNormal(topPoint.mult(scale), rootPoint.mult(scale), leftPoint.mult(scale));  + n1.set(calculateNormal(topPoint.mult(scale), rootPoint.mult(scale), leftPoint.mult(scale)));   }  - Vector3f n2 = Vector3f.ZERO;  + Vector3f n2 = new Vector3f(0,0,0);   if (leftPoint != null && bottomPoint != null) {  - n2 = calculateNormal(leftPoint.mult(scale), rootPoint.mult(scale), bottomPoint.mult(scale));  + n2.set(calculateNormal(leftPoint.mult(scale), rootPoint.mult(scale), bottomPoint.mult(scale)));   }  - Vector3f n3 = Vector3f.ZERO;  + Vector3f n3 = new Vector3f(0,0,0);   if (rightPoint != null && bottomPoint != null) {  - n3 = calculateNormal(bottomPoint.mult(scale), rootPoint.mult(scale), rightPoint.mult(scale));  + n3.set(calculateNormal(bottomPoint.mult(scale), rootPoint.mult(scale), rightPoint.mult(scale)));   }  - Vector3f n4 = Vector3f.ZERO;  + Vector3f n4 = new Vector3f(0,0,0);   if (rightPoint != null && topPoint != null) {  - n4 = calculateNormal(rightPoint.mult(scale), rootPoint.mult(scale), topPoint.mult(scale));  + n4.set(calculateNormal(rightPoint.mult(scale), rootPoint.mult(scale), topPoint.mult(scale)));   }    - if (bottomPoint != null && rightPoint != null && rootTex != null && rightTex != null && bottomTex != null)  - LODGeomap.calculateTangent(new Vector3f[]{rootPoint.mult(scale),rightPoint.mult(scale),bottomPoint.mult(scale)}, new Vector2f[]{rootTex,rightTex,bottomTex}, tangent, binormal);  + //if (bottomPoint != null && rightPoint != null && rootTex != null && rightTex != null && bottomTex != null)  + // LODGeomap.calculateTangent(new Vector3f[]{rootPoint.mult(scale),rightPoint.mult(scale),bottomPoint.mult(scale)}, new Vector2f[]{rootTex,rightTex,bottomTex}, tangent, binormal);    - normal.set(n1.add(n2).add(n3).add(n4).normalizeLocal());  + normal.set(n1.add(n2).add(n3).add(n4).normalize());  +  + tangent.set(normal.cross(new Vector3f(0,0,1)).normalize());  + binormal.set(new Vector3f(1,0,0).cross(normal).normalize());   }     private Vector3f calculateNormal(Vector3f firstPoint, Vector3f rootPoint, Vector3f secondPoint) {  
diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java index 0b9b218..1086c71 100644 --- a/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java +++ b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java 
@@ -1675,8 +1675,9 @@  }   }   }  - else  + else if (children.get(i) instanceof TerrainQuad) {   ((TerrainQuad) children.get(i)).findPick(toTest, results);  + }   }   }   }  
diff --git a/engine/src/test/jme3test/app/TestAppStateLifeCycle.java b/engine/src/test/jme3test/app/TestAppStateLifeCycle.java new file mode 100644 index 0000000..f224242 --- /dev/null +++ b/engine/src/test/jme3test/app/TestAppStateLifeCycle.java 
@@ -0,0 +1,126 @@ +/* + * Copyright (c) 2009-2012 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package jme3test.app; + +import com.jme3.app.Application; +import com.jme3.app.SimpleApplication; +import com.jme3.app.state.AbstractAppState; +import com.jme3.app.state.AppStateManager; +import com.jme3.material.Material; +import com.jme3.math.Vector3f; +import com.jme3.renderer.RenderManager; +import com.jme3.scene.Geometry; +import com.jme3.scene.shape.Box; + + +/** + * Tests the app state lifecycles. + * + * @author Paul Speed + */ +public class TestAppStateLifeCycle extends SimpleApplication { + + public static void main(String[] args){ + TestAppStateLifeCycle app = new TestAppStateLifeCycle(); + app.start(); + } + + @Override + public void simpleInitApp() { + Box b = new Box(Vector3f.ZERO, 1, 1, 1); + Geometry geom = new Geometry("Box", b); + Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + mat.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg")); + geom.setMaterial(mat); + rootNode.attachChild(geom); + + System.out.println("Attaching test state."); + stateManager.attach(new TestState());  + } + + @Override + public void simpleUpdate(float tpf) { +  + if(stateManager.getState(TestState.class) != null) { + System.out.println("Detaching test state.");  + stateManager.detach(stateManager.getState(TestState.class)); + System.out.println("Done");  + }  + } +  + public class TestState extends AbstractAppState { +  + @Override + public void initialize(AppStateManager stateManager, Application app) { + super.initialize(stateManager, app); + System.out.println("Initialized"); + } +  + @Override + public void stateAttached(AppStateManager stateManager) { + super.stateAttached(stateManager); + System.out.println("Attached"); + } +  + @Override + public void update(float tpf) { + super.update(tpf); + System.out.println("update"); + } + + @Override + public void render(RenderManager rm) { + super.render(rm); + System.out.println("render"); + } + + @Override + public void postRender() { + super.postRender(); + System.out.println("postRender"); + } + + @Override + public void stateDetached(AppStateManager stateManager) { + super.stateDetached(stateManager); + System.out.println("Detached"); + } +  + @Override + public void cleanup() { + super.cleanup(); + System.out.println("Cleanup");  + } + + }  +} 
diff --git a/engine/src/test/jme3test/effect/TestSoftParticles.java b/engine/src/test/jme3test/effect/TestSoftParticles.java new file mode 100644 index 0000000..d779a6f --- /dev/null +++ b/engine/src/test/jme3test/effect/TestSoftParticles.java 
@@ -0,0 +1,130 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package jme3test.effect; + +import com.jme3.app.SimpleApplication; +import com.jme3.effect.ParticleEmitter; +import com.jme3.effect.ParticleMesh; +import com.jme3.effect.shapes.EmitterSphereShape; +import com.jme3.input.KeyInput; +import com.jme3.input.controls.ActionListener; +import com.jme3.input.controls.KeyTrigger; +import com.jme3.material.Material; +import com.jme3.math.ColorRGBA; +import com.jme3.math.Quaternion; +import com.jme3.math.Vector3f; +import com.jme3.post.FilterPostProcessor; +import com.jme3.post.filters.TranslucentBucketFilter; +import com.jme3.scene.Geometry; +import com.jme3.scene.shape.Box; + +/** + * + * @author Nehon + */ +public class TestSoftParticles extends SimpleApplication { + + private boolean softParticles = true; + private FilterPostProcessor fpp; + private TranslucentBucketFilter tbf; + + public static void main(String[] args) { + TestSoftParticles app = new TestSoftParticles(); + app.start(); + } + + @Override + public void simpleInitApp() { + + cam.setLocation(new Vector3f(-7.2221026f, 4.1183004f, 7.759811f)); + cam.setRotation(new Quaternion(0.06152846f, 0.91236454f, -0.1492115f, 0.37621948f)); + + flyCam.setMoveSpeed(10); + + + // -------- floor + Box b = new Box(Vector3f.ZERO, 10, 0.1f, 10); + Geometry geom = new Geometry("Box", b); + Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + mat.setColor("Color", ColorRGBA.Gray); + mat.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg")); + geom.setMaterial(mat); + rootNode.attachChild(geom); + + Box b2 = new Box(Vector3f.ZERO, 1, 1, 1); + Geometry geom2 = new Geometry("Box", b2); + Material mat2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + mat2.setColor("Color", ColorRGBA.DarkGray); + geom2.setMaterial(mat2); + rootNode.attachChild(geom2); + geom2.setLocalScale(0.1f, 0.2f, 1); + + fpp = new FilterPostProcessor(assetManager);  + tbf = new TranslucentBucketFilter(true); + fpp.addFilter(tbf); + viewPort.addProcessor(fpp); + + + Material material = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md"); + material.setTexture("Texture", assetManager.loadTexture("Effects/Explosion/flame.png")); +  + material.setFloat("Softness", 3f); //  + + + //Fire + ParticleEmitter fire = new ParticleEmitter("Fire", ParticleMesh.Type.Triangle, 30); + fire.setMaterial(material); + fire.setShape(new EmitterSphereShape(Vector3f.ZERO, 0.1f)); + fire.setImagesX(2); + fire.setImagesY(2); // 2x2 texture animation + fire.setEndColor(new ColorRGBA(1f, 0f, 0f, 1f)); // red + fire.setStartColor(new ColorRGBA(1f, 1f, 0f, 0.5f)); // yellow + fire.setStartSize(0.6f); + fire.setEndSize(0.01f); + fire.setGravity(0, -0.3f, 0); + fire.setLowLife(0.5f); + fire.setHighLife(3f); + fire.setLocalTranslation(0, 0.2f, 0); + + rootNode.attachChild(fire); +  +  + ParticleEmitter smoke = new ParticleEmitter("Smoke", ParticleMesh.Type.Triangle, 30); + smoke.setMaterial(material); + smoke.setShape(new EmitterSphereShape(Vector3f.ZERO, 5)); + smoke.setImagesX(1); + smoke.setImagesY(1); // 2x2 texture animation + smoke.setStartColor(new ColorRGBA(0.1f, 0.1f, 0.1f,1f)); // dark gray + smoke.setEndColor(new ColorRGBA(0.5f, 0.5f, 0.5f, 0.3f)); // gray  + smoke.setStartSize(3f); + smoke.setEndSize(5f); + smoke.setGravity(0, -0.001f, 0); + smoke.setLowLife(100f); + smoke.setHighLife(100f); + smoke.setLocalTranslation(0, 0.1f, 0);  + smoke.emitAllParticles(); +  + rootNode.attachChild(smoke); + +  + inputManager.addListener(new ActionListener() { + + public void onAction(String name, boolean isPressed, float tpf) { + if(isPressed && name.equals("toggle")){ + // tbf.setEnabled(!tbf.isEnabled());  + softParticles = !softParticles; + if(softParticles){ + viewPort.addProcessor(fpp); + }else{ + viewPort.removeProcessor(fpp); + } + } + } + }, "toggle"); + inputManager.addMapping("toggle", new KeyTrigger(KeyInput.KEY_SPACE)); + } +  +  +} 
diff --git a/engine/src/test/jme3test/helloworld/HelloMaterial.java b/engine/src/test/jme3test/helloworld/HelloMaterial.java index e930c91..8ef6f56 100644 --- a/engine/src/test/jme3test/helloworld/HelloMaterial.java +++ b/engine/src/test/jme3test/helloworld/HelloMaterial.java 
@@ -80,7 +80,7 @@  /** A cube with its base color "leaking" through a partially transparent texture */  Box boxshape4 = new Box(new Vector3f(3f,-1f,0f), 1f,1f,1f);  Geometry cube_leak = new Geometry("Leak-through color cube", boxshape4); - Material mat_tl = new Material(assetManager, "Common/MatDefs/Misc/ColoredTextured.j3md"); + Material mat_tl = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");  mat_tl.setTexture("ColorMap", assetManager.loadTexture("Textures/ColoredTex/Monkey.png"));  mat_tl.setColor("Color", new ColorRGBA(1f,0f,1f, 1f)); // purple  cube_leak.setMaterial(mat_tl); @@ -103,6 +103,7 @@  shiny_rock.setLocalTranslation(0,2,-2); // Move it a bit  shiny_rock.rotate(1.6f, 0, 0); // Rotate it a bit  rootNode.attachChild(shiny_rock); +   /** Must add a light to make the lit object visible! */  DirectionalLight sun = new DirectionalLight();  sun.setDirection(new Vector3f(1,0,-2).normalizeLocal()); 
diff --git a/engine/src/test/jme3test/model/shape/TestExpandingTorus.java b/engine/src/test/jme3test/model/shape/TestExpandingTorus.java new file mode 100644 index 0000000..9e76b3f --- /dev/null +++ b/engine/src/test/jme3test/model/shape/TestExpandingTorus.java 
@@ -0,0 +1,45 @@ +/*  + * To change this template, choose Tools | Templates  + * and open the template in the editor.  + */  +package jme3test.model.shape;  +  +import com.jme3.app.SimpleApplication;  +import com.jme3.material.Material;  +import com.jme3.scene.Geometry;  +import com.jme3.scene.shape.Torus;  +  +public class TestExpandingTorus extends SimpleApplication {  +  + private float outerRadius = 1.5f;  + private float rate = 1;  + private Torus torus;  + private Geometry geom;  +  + public static void main(String[] args) {  + TestExpandingTorus app = new TestExpandingTorus();  + app.start();  + }  +  + @Override  + public void simpleInitApp() {  + torus = new Torus(30, 10, .5f, 1f);  + geom = new Geometry("Torus", torus);  + Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");  + geom.setMaterial(mat);  + rootNode.attachChild(geom);  + }  +  + @Override  + public void simpleUpdate(float tpf){  + if (outerRadius > 2.5f){  + outerRadius = 2.5f;  + rate = -rate;  + }else if (outerRadius < 1f){  + outerRadius = 1f;  + rate = -rate;  + }  + outerRadius += rate * tpf;  + torus.updateGeometry(30, 10, .5f, outerRadius);  + }  +} \ No newline at end of file 
diff --git a/engine/src/tools/jme3tools/converters/model/ModelConverter.java b/engine/src/tools/jme3tools/converters/model/ModelConverter.java index 2539574..6383d43 100644 --- a/engine/src/tools/jme3tools/converters/model/ModelConverter.java +++ b/engine/src/tools/jme3tools/converters/model/ModelConverter.java 
@@ -69,7 +69,7 @@  ts.setListsOnly(listOnly);  ts.setMinStripSize(minStripSize);   - IndexBuffer ib = mesh.getIndexBuffer(); + IndexBuffer ib = mesh.getIndicesAsList();  int[] indices = new int[ib.size()];  for (int i = 0; i < indices.length; i++)  indices[i] = ib.get(i); @@ -157,7 +157,7 @@  mesh.setBuffer(vb);  }  } - mesh.setInterleaved(); + //mesh.setInterleaved();  }    private static void optimizeScene(Spatial source, boolean toFixed){ 
diff --git a/engine/src/tools/jme3tools/optimize/GeometryBatchFactory.java b/engine/src/tools/jme3tools/optimize/GeometryBatchFactory.java index ae6ad8c..121d75c 100644 --- a/engine/src/tools/jme3tools/optimize/GeometryBatchFactory.java +++ b/engine/src/tools/jme3tools/optimize/GeometryBatchFactory.java 
@@ -62,26 +62,27 @@  }  }   - private static void doTransformTangents(FloatBuffer inBuf, int offset, FloatBuffer outBuf, Matrix4f transform) { + private static void doTransformTangents(FloatBuffer inBuf, int offset, int components, FloatBuffer outBuf, Matrix4f transform) {  Vector3f tan = new Vector3f(); - float handedness = 0; +   // offset is given in element units  // convert to be in component units - offset *= 4; + offset *= components;   - for (int i = 0; i < inBuf.capacity() / 4; i++) { - tan.x = inBuf.get(i * 4 + 0); - tan.y = inBuf.get(i * 4 + 1); - tan.z = inBuf.get(i * 4 + 2); - handedness = inBuf.get(i * 4 + 3); + for (int i = 0; i < inBuf.capacity() / components; i++) { + tan.x = inBuf.get(i * components + 0); + tan.y = inBuf.get(i * components + 1); + tan.z = inBuf.get(i * components + 2);    transform.multNormal(tan, tan);   - outBuf.put(offset + i * 4 + 0, tan.x); - outBuf.put(offset + i * 4 + 1, tan.y); - outBuf.put(offset + i * 4 + 2, tan.z); - outBuf.put(offset + i * 4 + 3, handedness); -  + outBuf.put(offset + i * components + 0, tan.x); + outBuf.put(offset + i * components + 1, tan.y); + outBuf.put(offset + i * components + 2, tan.z); +  + if (components == 4){ + outBuf.put(offset + i * components + 3, inBuf.get(i * components + 3)); + }  }  }   @@ -129,9 +130,9 @@  throw new UnsupportedOperationException();  }   - for (Entry<VertexBuffer> entry : geom.getMesh().getBuffers()) { - compsForBuf[entry.getKey()] = entry.getValue().getNumComponents(); - formatForBuf[entry.getKey()] = entry.getValue().getFormat(); + for (VertexBuffer vb : geom.getMesh().getBufferList().getArray()){ + compsForBuf[vb.getBufferType().ordinal()] = vb.getNumComponents(); + formatForBuf[vb.getBufferType().ordinal()] = vb.getFormat();  }    if (mode != null && mode != listMode) { @@ -208,10 +209,11 @@  FloatBuffer inPos = (FloatBuffer) inBuf.getDataReadOnly();  FloatBuffer outPos = (FloatBuffer) outBuf.getData();  doTransformNorms(inPos, globalVertIndex, outPos, worldMatrix); - }else if(Type.Tangent.ordinal() == bufType){  + }else if(Type.Tangent.ordinal() == bufType){   FloatBuffer inPos = (FloatBuffer) inBuf.getDataReadOnly();  FloatBuffer outPos = (FloatBuffer) outBuf.getData(); - doTransformTangents(inPos, globalVertIndex, outPos, worldMatrix); + int components = inBuf.getNumComponents(); + doTransformTangents(inPos, globalVertIndex, components, outPos, worldMatrix);  } else {  inBuf.copyElements(0, outBuf, globalVertIndex, geomVertCount);  }